IPv6:getaddrinfo返回地址信息(addrinfo)顺序问题

IPv6:getaddrinfo返回地址信息(addrinfo)顺序问题

#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或者全部尝试完毕。

你可能感兴趣的:(C-C++,NETWORK,LINUX)