域名解析函数getaddrinfo()
#在使用socket进行网络通信时,可能需要将主机名(例如www.example.com)解析成IP地址(例如192.0.2.1),或者将IP地址转换为主机名。这些操作可以使用以下两个函数之一进行:
getaddrinfo函数:将主机名和服务名称(例如“http”)转换为一个或多个套接字地址结构。
getnameinfo函数:将套接字地址结构转换为主机名和服务名称。
以下是getaddrinfo函数的详细说明:
getaddrinfo函数
函数原型如下:
#include <netdb.h>
int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints, struct addrinfo **res);
参数说明:
node:主机名或IP地址字符串,如果为NULL,则表示本地主机。
service:服务名称或端口号字符串,如果为NULL,则返回所有可用的套接字地址结构。
hints:用于指定期望的套接字类型、协议及其他选项的addrinfo结构体指针。
res:用于存储结果的addrinfo结构体指针。
该函数将主机名和服务名称转换为一个或多个套接字地址结构,并将结果存储在指向addrinfo结构体的指针中。在调用该函数之前,应该先初始化hints结构体并设置所需的选项。例如,如果要连接到远程服务器的80端口,则hints结构体应该设置为:
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // 使用IPv4地址
hints.ai_socktype = SOCK_STREAM; // 使用TCP协议
此外,调用者还应该检查函数返回值是否为0。如果函数返回0,则表示操作成功;否则,调用者应该通过gai_strerror函数将错误码转换为可读的字符串进行诊断。
在使用完返回的addrinfo结构体之后,应该调用freeaddrinfo函数释放它们的内存。
以下是一个示例,演示了如何使用getaddrinfo函数将主机名解析为IP地址:
代码演示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]) {
struct addrinfo hints, *result, *p; // 定义addrinfo结构体变量
int status; // getaddrinfo函数返回值
char ipstr[INET6_ADDRSTRLEN]; // 存储IP地址字符串的缓冲区
if (argc != 2) { // 检查命令行参数数量是否正确
fprintf(stderr, "Usage: %s hostname\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(hints)); // 初始化hints结构体
hints.ai_family = AF_UNSPEC; // 不限制IP地址版本
hints.ai_socktype = SOCK_STREAM; // 使用TCP协议
if ((status = getaddrinfo(argv[1], NULL, &hints, &result)) != 0) { // 解析主机名并将结果存储在result指针中
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(EXIT_FAILURE);
}
printf("IP addresses for %s:\n\n", argv[1]);
for (p = result; p != NULL; p = p->ai_next) { // 遍历result指针中的所有套接字地址结构
void *addr;
char *ipver;
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)); // 将套接字地址结构转换为IP地址字符串
printf(" %s: %s\n", ipver, ipstr); // 打印IP地址和版本号
}
freeaddrinfo(result); // 释放由getaddrinfo函数分配的内存
return EXIT_SUCCESS; // 程序正常退出
}
代码分析
在上面的代码中,我们使用getaddrinfo函数将命令行参数指定的主机名解析为IP地址,并逐个打印出所有解析结果。
首先,我们检查命令行参数数量是否正确。然后,初始化hints结构体,并将其ai_family字段设置为AF_UNSPEC,以不限制IP地址版本。接下来,调用getaddrinfo函数,将主机名和其他选项作为输入参数,并将结果存储在result指针中。
然后,我们遍历result指针中的所有套接字地址结构,并逐个打印出与之对应的IP地址和版本号。在打印前,我们使用inet_ntop函数将套接字地址结构转换为IP地址字符串,并将结果存储在ipstr缓冲区中。
最后,我们调用freeaddrinfo函数释放由getaddrinfo函数分配的内存,然后正常退出程序。