#include
#include
#include
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
我的目的是创建一个服务端socket(IPV6),接收tcp/udp消息。
通常来说,addrinfo中ai_family我们最常使用的是AF_INET(IPv4)以及AF_INET6(IPv6)。
尝试执行以下程序:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_DGRAM;
struct addrinfo *res;
char *port = "4020";
int e = getaddrinfo(NULL, port, &hints, &res);
if (e == EAI_SYSTEM)
{
printf("getaddrinfo error: %s\n", strerror(errno));
exit(1);
}
else if (e != 0)
{
printf("getaddrinfo error: %s\n", gai_strerror(e));
exit(2);
}
struct addrinfo *p = res;
while (p != NULL)
{
if (p->ai_family == AF_INET)
{
printf("p->ai_family == AF_INET\n");
}
else if (p->ai_family == AF_INET6)
{
printf("p->ai_family == AF_INET6\n");
}
else
{
printf("UNKNOWN\n");
}
p = p->ai_next;
}
freeaddrinfo(res);
exit(0);
}
输出结果为:
[udpdriver@eb6347 0328]$ gcc -o main main.c
[udpdriver@eb6347 0328]$ ./main
p->ai_family == AF_INET6
p->ai_family == AF_INET
即,先返回IPv6的地址,然后返回IPv4的地址。
如果经过测试,会发现基本所有(NOT ALL)Linux都是先返回IPv6,然后返回IPv4的地址,似乎底层实现都是这么干得。
但实际也可能出现:先返回IPv4的地址,然后返回IPv6的地址。
在另一个Linux主机上执行以上程序,输出如下:
[isms@localhost 0328]$ ./main
p->ai_family == AF_INET
p->ai_family == AF_INET6
man手册中也并未定义,IPv6的addrinfo一定在IPv4的前面。
所以,我们要遍历addrinfo链表。
我们应当将addrinfo分成两部分,一部分是IPv6的(AF_INET6),一部分是IPv4的(AF_INET)地址信息。
我们应该优先尝试使用IPv6的地址信息创建socket,一旦成功则直接返回;
如果所有的IPv6的地址信息创建socket都失败,则我们应当以IPv4的地址信息依次创建socket,一旦成功则直接返回;
如果遍历所有的地址信息,都创建socket失败,则socket创建失败。
即:我们不能假定,通过getaddrinfo得到的地址信息链表,IPv6总是优先于IPv4排列。
另外,由getaddrinfo返回的地址信息,也并非全都是有效的、可用的,有可能返回不符合系统实际的地址信息,这就会导致创建socket失败。
例如:
在HP9000平台上,有时未开启IPv6服务,此时,我们通过getaddrinfo获得一个addrinfo,其中ai_family=AF_INET6。
如果你拿这个addrinfo去创建socket,则在运行期创建socket失败(socket系统调用失败),失败原因是:
Protocol not supported
即:getaddrinfo返回给你地址信息,可能由于某些服务未开启或者配置等问题,导致创建socket失败。
其返回的地址信息,也可能是当前系统不支持的协议类型等。
总之,我们应当在创建socket时多次尝试,优先尝试,直到创建成功一个socket或者全部尝试完毕。