1. getaddrinfo能做什么
- 名字到地址转换:解析DNS地址
- 服务到端口转换:将IP地址和端口转换为一个socket地址
2. 重要数据结构
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
- ai_family
- AF_INET:IPv4
- AF_INET6:IPv6
- AF_UNSPEC:协议无关
- ai_protocol
- IPPROTO_IP:IP协议
- IPPROTO_IPV4:IPv4协议
- IPPROTO_IPV6:IPv6协议
- IPPROTO_UDP:UDP
- IPPROTO_TCP:TCP
- ai_socktype
- SOCK_STREAM
- SOCK_DGRAM
- ai_flags
- AI_PASSIVE:被动的,常用于bind和accept,用于server端
- AI_CANONNAME:返回主机的规范名称
- AI_NUMERICHOST:地址为数字
3. getaddrinfo原型
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
- node可选值
- 点分十进制IPv4地址
- IPv6地址
- DNS地址
- 当node为DNS地址时,有时会遇到getaddrinfo返回过慢的问题,为什么?
- 当getaddrinfo用来解析DNS时,底层实现使用res_nxxx系列接口(如:res_nsearch)
- 当node为DNS地址时,有时会遇到getaddrinfo返回过慢的问题,为什么?
- 影响getaddrinfo行为的hints.ai_flags值
- 当为AI_PASSIVE,node为NULL,server为一个端口,则返回一个0.0.0.0:port对应的socket地址,该地址可用于bind和accept
- 当不设置AI_PASSIVE时,node不为NULL,则返回一个可用于connect、sendto或sendmsg的socket地址;如果node为NULL,则返回127.0.0.1的socket地址
- service:设置socket地址对应的端口号。如果service为NULL,则端口默认被初始化为0
- res:返回一个指向addrinfo结构体链表
注:node或service中任何一个可以为NULL,但不能两者都为NULL
4. 用例
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(int argc, const char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Please input a valid address!\n");
return -1;
}
char buffer[256] = {0};
struct addrinfo hints = {0}, *result = NULL, *tmp = NULL;
struct sockaddr_in *addr = NULL;
struct sockaddr_in6 *addr6 = NULL;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_INET;
hints.ai_flags = 0;
hints.ai_protocol = 0;
int ret = getaddrinfo(argv[1], NULL, &hints, &result);
if (ret) {
fprintf(stderr, "errno: %d: %s\n", ret, gai_strerror(ret));
return -1;
}
for (tmp = result; tmp; tmp = tmp->ai_next) {
if (AF_INET == tmp->ai_family) {
addr = (struct sockaddr_in*)tmp->ai_addr;
printf("IPv4 Address: %s:%d\n", inet_ntop(AF_INET, &addr->sin_addr, buffer, sizeof(buffer)), htons(addr->sin_port));
} else {
addr6 = (struct sockaddr_in6*)tmp->ai_addr;
printf("IPv6 Address: %s:%d\n", inet_ntop(AF_INET6, &addr6->sin6_addr, buffer, sizeof(buffer)), htons(addr6->sin6_port));
}
}
freeaddrinfo(result);
return 0;
}