getaddrinfo
是一个用于将主机名和服务名解析为套接字地址结构的函数,它在网络编程中起着关键作用。以下是关于 getaddrinfo
函数的一些关键点:
-
协议无关性:
getaddrinfo
是一个协议无关的函数,既可以用于IPv4也可用于IPv6,这使得它比早期的gethostbyname
函数更加灵活和强大。 -
功能:该函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个
addrinfo
结构体(列表)指针,而不是单一的地址。 -
参数:
nodename
:可以是主机名或者数字化的地址字符串(IPv4的点分十进制串或者IPv6的16进制串)。servname
:服务名可以是十进制的端口号,也可以是已定义的服务名称,如"ftp"、"http"等。hints
:是一个addrinfo
结构体的指针,由调用者填写关于期望返回的信息类型的提示。res
:返回结果,指向一个addrinfo
结构体链表的指针。
-
返回值:成功时返回0,失败时返回非0错误码,可以通过
gai_strerror
函数获取错误信息。 -
使用细节:如果函数返回成功,那么
res
参数指向的变量将被填入一个指针,指向由ai_next
成员串联起来的addrinfo
结构链表。可能返回多个addrinfo
结构的情况包括:与hostname
参数关联的地址有多个,或者service
参数指定的服务支持多个套接口类型。 -
内存管理:由
getaddrinfo
返回的所有存储空间都是动态分配的,必须通过调用freeaddrinfo
函数释放。 -
应用场景:
getaddrinfo
在网络编程中有广泛的应用,包括建立TCP/UDP连接、实现DNS查询、处理多IP地址等。
综上所述,getaddrinfo
是一个功能全面、支持多种协议的地址解析函数,适用于现代网络编程中的多种场景。
getaddrinfo
函数的使用涉及到几个步骤,包括设置 hints
结构体、调用 getaddrinfo
函数、处理返回的 addrinfo
结构体链表,以及释放内存。下面是一个使用 getaddrinfo
函数的简单示例,它展示了如何将主机名和端口号解析为套接字地址结构:
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main() {
const char *node = "example.com";
const char *service = "http";
struct addrinfo hints, *res, *p;
// 初始化 hints 结构体
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // 不指定是IPv4还是IPv6
hints.ai_socktype = SOCK_STREAM; // 我们想要的是流式套接字(例如TCP)
// 调用 getaddrinfo 函数
int status = getaddrinfo(node, service, &hints, &res);
if (status != 0) {
std::cerr << "getaddrinfo error: " << gai_strerror(status) << std::endl;
return 1;
}
// 遍历 addrinfo 结构体链表
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
char ipstr[INET6_ADDRSTRLEN]; // INET6_ADDRSTRLEN 是 IPv6 地址的最大长度
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = reinterpret_cast<struct sockaddr_in *>(p->ai_addr);
addr = &(ipv4->sin_addr);
} else { // IPv6
struct sockaddr_in6 *ipv6 = reinterpret_cast<struct sockaddr_in6 *>(p->ai_addr);
addr = &(ipv6->sin6_addr);
}
// 将地址转换成字符串形式
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
std::cout << "IP Address: " << ipstr << std::endl;
}
// 释放 getaddrinfo 分配的内存
freeaddrinfo(res);
return 0;
}
步骤解释:
-
初始化
hints
结构体:memset(&hints, 0, sizeof hints);
:将hints
结构体清零。hints.ai_family = AF_UNSPEC;
:不指定地址族,可以是IPv4或IPv6。hints.ai_socktype = SOCK_STREAM;
:指定我们想要的是流式套接字(例如TCP)。
-
调用
getaddrinfo
函数:getaddrinfo(node, service, &hints, &res);
:将主机名和端口号解析为套接字地址结构。- 如果返回值不为0,则输出错误信息并退出。
-
遍历
addrinfo
结构体链表:- 使用
for
循环遍历返回的addrinfo
结构体链表。 - 根据地址族(IPv4或IPv6),将地址转换为字符串形式,并输出。
- 使用
-
释放内存:
- 使用
freeaddrinfo(res);
释放getaddrinfo
分配的内存。
- 使用
这个示例展示了如何使用 getaddrinfo
函数将主机名和端口号解析为套接字地址结构,并输出解析后的IP地址。希望这个示例能帮助你理解 getaddrinfo
的使用方式。