在C语言中,获取域名和端口号是网络编程中的常见需求,以下是一些详细的方法介绍:
1、获取本地端口号
使用getsockname函数:
该函数用于获取与指定套接字关联的本地地址,其原型为int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
。
示例代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> int main() { int server_fd; struct sockaddr_in address; int addrlen = sizeof(address); if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(0); // 让系统自动分配端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } // 获取本地地址 if (getsockname(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen) == -1) { perror("getsockname failed"); close(server_fd); exit(EXIT_FAILURE); } printf("Local port: %d ", ntohs(address.sin_port)); return 0; }
在这个示例中,首先创建了一个套接字并绑定到一个任意端口(通过将sin_port
设置为0),使用getsockname
函数获取绑定的端口号,并通过ntohs
函数将其转换为主机字节序进行打印。
2、获取远端端口号
使用getpeername函数:
此函数用于获取与指定套接字关联的远端地址,其原型为int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
。
示例代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define PORT 8080 int main() { int server_fd, new_socket; struct sockaddr_in address, client_addr; int addrlen = sizeof(address); int client_addrlen = sizeof(client_addr); // 创建套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置地址和端口 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定套接字 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } // 监听套接字 if (listen(server_fd, 3) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } // 接受客户端连接 if ((new_socket = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t*)&client_addrlen))<0) { perror("accept"); close(server_fd); exit(EXIT_FAILURE); } // 获取远端地址 if (getpeername(new_socket, (struct sockaddr *)&client_addr, (socklen_t*)&client_addrlen) == -1) { perror("getpeername failed"); close(new_socket); close(server_fd); exit(EXIT_FAILURE); } printf("Remote port: %d ", ntohs(client_addr.sin_port)); close(new_socket); close(server_fd); return 0; }
上述代码中,服务器先创建一个套接字并绑定到指定端口,然后开始监听,当有客户端连接时,使用accept
函数接受连接,并通过getpeername
函数获取客户端的远端端口号。
3、获取域名对应的IP地址及端口号(如果已知服务类型)
使用getaddrinfo函数:
getaddrinfo
函数是POSIX标准的一部分,它提供了一种独立于协议的方式来解析主机名和服务名,其原型为int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
,其中node
是要解析的主机名或IP地址字符串,service
是服务名或端口号字符串,hints
是一个可选的addrinfo
结构体,用于提供输入数据,res
是一个指向addrinfo
结构体链表的指针,用于输出结果。
示例代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> int main() { struct addrinfo hints, *res; int status; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // 允许IPv4或IPv6 hints.ai_socktype = SOCK_STREAM; // TCP流 status = getaddrinfo("www.example.com", "http", &hints, &res); if (status != 0) { fprintf(stderr, "getaddrinfo error: %s ", gai_strerror(status)); return 1; } for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { void *addr; char *ipver; char ipstr[INET6_ADDRSTRLEN]; if (p->ai_family == AF_INET) { // IPv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } else { // IPv6 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); ipver = "IPv6"; } // 将地址转换为字符串形式 inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); printf("%s: %s ", ipver, ipstr); } freeaddrinfo(res); // 释放内存 return 0; }
在这个例子中,通过调用getaddrinfo
函数解析域名www.example.com
的IP地址,并根据服务名http
确定端口号(HTTP默认端口为80),然后遍历返回的addrinfo
结构体链表,提取并打印IP地址信息,如果是其他服务,可以将service
参数修改为相应的服务名或端口号字符串。
在C语言中获取域名和端口号需要根据具体的需求和场景选择合适的方法,无论是获取本地端口号、远端端口号还是通过域名获取IP地址及端口号,都需要正确使用相关的系统调用和库函数,并进行适当的错误处理,以确保程序的稳定性和可靠性。