addrinfo结构体与getaddrinfo函数

来源:互联网 发布:女生做数据运营 编辑:程序博客网 时间:2024/06/11 07:50

    addrinfo结构体的定义如下:

    1. struct addrinfo {
    1.      int ai_flags; /* customize behavior */
    2.      int ai_family; /* address family */
    3.      int ai_socktype; /* socket type */
    4.      int ai_protocol; /* protocol */
    5.      socklen_t ai_addrlen; /* length in bytes of address */
    6.      struct sockaddr *ai_addr; /* address */
    7.      char *ai_canonname; /* canonical name of host */
    8.      struct addrinfo *ai_next; /* next in list */
    9.      .
    10.      .
    11.      .
    12.    };

    ai_family指定了地址族,可取值如下: 

    AF_INET          2            IPv4 

    AF_INET6        23            IPv6 

    AF_UNSPEC        0            协议无关

    ai_socktype指定我套接字的类型 

    SOCK_STREAM        1            流 

    SOCK_DGRAM        2            数据报

    在AF_INET通信域中套接字类型SOCK_STREAM的默认协议是TCP(传输控制协议)

    在AF_INET通信域中套接字类型SOCK_DGRAM的默认协议是UDP(用户数据报协议)

    ai_protocol指定协议类型。可取的值取决于ai_address和ai_socktype的值

    ai_flags指定了如何来处理地址和名字,可取值如下:

    QQ截图20120409193145

    getaddrinfo函数 定义及需要的头文件如下:

    1. #include <sys/socket.h>
    1. #include <netdb.h>
    1. int getaddrinfo(const char *restrict host,
    1.                 const char *restrict service,
    2.                 const struct addrinfo *restrict hint,
    3.                 struct addrinfo **restrict res);
    4. Returns: 0 if OK, nonzero error code on error
    5. void freeaddrinfo(struct addrinfo *ai);

    getaddrinfo函数允许将一个主机名字和服务名字映射到一个地址。

    使用示例如下:

    1. #include<stdio.h>
    1. #include<stdlib.h>
    2. #include <netdb.h>
    3. #include <arpa/inet.h>
    4. void
    5. print_family(struct addrinfo *aip)
    1. {
    1.     printf(" family ");
    2.     switch (aip->ai_family) {
    3.     case AF_INET:
    4.         printf("inet");
    5.         break;
    6.     case AF_INET6:
    7.         printf("inet6");
    8.         break;
    9.     case AF_UNIX:
    10.         printf("unix");
    11.         break;
    12.     case AF_UNSPEC:
    13.         printf("unspecified");
    14.         break;
    15.     default:
    16.         printf("unknown");
    17.     }
    1. }
    1. void
    2. print_type(struct addrinfo *aip)
    1. {
    1.     printf(" type ");
    2.     switch (aip->ai_socktype) {
    3.     case SOCK_STREAM:
    4.         printf("stream");
    5.         break;
    6.     case SOCK_DGRAM:
    7.         printf("datagram");
    8.         break;
    9.     case SOCK_SEQPACKET:
    10.         printf("seqpacket");
    11.         break;
    12.     case SOCK_RAW:
    13.         printf("raw");
    14.         break;
    15.     default:
    16.         printf("unknown (%d)", aip->ai_socktype);
    17.     }
    1. }
    1. void
    2. print_protocol(struct addrinfo *aip)
    1. {
    1.     printf(" protocol ");
    2.     switch (aip->ai_protocol) {
    3.     case 0:
    4.         printf("default");
    5.         break;
    6.     case IPPROTO_TCP:
    7.         printf("TCP");
    8.         break;
    9.     case IPPROTO_UDP:
    10.         printf("UDP");
    11.         break;
    12.     case IPPROTO_RAW:
    13.         printf("raw");
    14.         break;
    15.     default:
    16.         printf("unknown (%d)", aip->ai_protocol);
    17.     }
    1. }
    1. void
    2. print_flags(struct addrinfo *aip)
    1. {
    1.     printf("flags");
    2.     if (aip->ai_flags == 0) {
    3.         printf(" 0");
    4.     } else {
    5.         if (aip->ai_flags & AI_PASSIVE)
    6.             printf(" passive");
    7.         if (aip->ai_flags & AI_CANONNAME)
    8.             printf(" canon");
    9.         if (aip->ai_flags & AI_NUMERICHOST)
    10.             printf(" numhost");
    11. #if defined(AI_NUMERICSERV)
    12.         if (aip->ai_flags & AI_NUMERICSERV)
    13.             printf(" numserv");
    14. #endif
    15. #if defined(AI_V4MAPPED)
    16.         if (aip->ai_flags & AI_V4MAPPED)
    17.             printf(" v4mapped");
    18. #endif
    19. #if defined(AI_ALL)
    20.         if (aip->ai_flags & AI_ALL)
    21.             printf(" all");
    22. #endif
    23.     }
    1. }
    1. int
    1. main(int argc, char *argv[])
    1. {
    1.     struct addrinfo        *ailist, *aip;
    2.     struct addrinfo        hint;
    3.     struct sockaddr_in    *sinp;
    4.     const char             *addr;
    5.     int                 err;
    6.     char                abuf[INET_ADDRSTRLEN];
    7.     if (argc != 3)
    8.         printf("usage: %s nodename service", argv[0]);
    9.     hint.ai_flags = AI_CANONNAME;
    10.     hint.ai_family = 0;
    11.     hint.ai_socktype = 0;
    12.     hint.ai_protocol = 0;
    13.     hint.ai_addrlen = 0;
    14.     hint.ai_canonname = NULL;
    15.     hint.ai_addr = NULL;
    16.     hint.ai_next = NULL;
    17.     if ((err = getaddrinfo(argv[1], argv[2], &hint, &ailist)) != 0)
    18.         printf("getaddrinfo error: %s", gai_strerror(err));
    19.     for (aip = ailist; aip != NULL; aip = aip->ai_next) {
    20.         print_flags(aip);
    21.         print_family(aip);
    22.         print_type(aip);
    23.         print_protocol(aip);
    24.         printf("\n\thost %s", aip->ai_canonname?aip->ai_canonname:"-");
    25.         if (aip->ai_family == AF_INET) {
    26.             sinp = (struct sockaddr_in *)aip->ai_addr;
    27.             addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf,INET_ADDRSTRLEN);
    28.             printf(" address %s", addr?addr:"unknown");
    29.             printf(" port %d", ntohs(sinp->sin_port));
    30.         }
    31.         printf("\n");
    32.     }
    33.     exit(0);
    1. }

    代码说明:sinp = (struct sockaddr_in *)aip->ai_addr;会将struct sockaddr 变量强制转化为struct sockaddr_in 类型

    inet_ntop函数用于在二进制格式与点分十进制格式表示(a.b.c.d)之间进行转换

    执行结果:#./a X86-PC nfs

    1. root@X86-PC:/home/cheney/work-link/linux_C_Test/linux-program/fig16# ./a X86-PC nfs
    2. flags canon family inet type stream protocol TCP
    3. host X86-PC address 127.0.1.1 port 2049
    4. flags canon family inet type datagram protocol UDP
    5. host - address 127.0.1.1 port 2049
    6. root@X86-PC:/home/cheney/work-link/linux_C_Test/linux-program/fig16#

     

    来自 <http://blog.chinaunix.net/uid-26583794-id-3167485.html>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    getaddrinfo()详解

    IPv4中使用gethostbyname()函数完成主机名到地址解析,但是该API不允许调用者指定所需地址类型的任何信息,返回的结构只包含 了用于存储IPv4地址的空间。为了解决该问题,IPv6中引入了getaddrinfo()的新API,它是协议无关的,既可用于IPv4也可用于 IPv6。调用该函数会获得一个addrinfo结构的列表,调用的返回值是addrinfo的结构(列表)指针。

            本文结合在WinowsXP和Windows2003 Server上使用该函数的经验,对getaddrinfo函数和addrinfo数据结构进行介绍,并对其参数的设置加以讨论,主要包括 nodename和servname的取值对返回值的影响,hints成员变量的设置对返回值的影响等。

    可能有不完全或不准确的地方,欢迎大家讨论并指出。

    1getaddrinfo函数原型

     

    函数

    参数说明

    int getaddrinfo(

    const char* nodename

    const char* servname,

    const struct addrinfo* hints,//

    struct addrinfo** res

    );

    nodename:节点名可以是主机名,也可以是数字地址。(IPV4的10进点分,或是IPV6的16进制)

    servname:包含十进制数的端口号或服务名如(ftp,http)

    hints:是一个空指针或指向一个addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索。

    res:存放返回addrinfo结构链表的指针

    Getaddrinfo提供独立于协议的名称解析。

    函数的前两个参数分别是节点名和服务名。节点名可以是主机名,也可以是地址串(IPv4的点分十进制数表示或IPv6的十六进制数字串)。服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等。注意:其中节点名和服务名都是可选项,即节点名或服务名可以为NULL,此时调用的结果将取缺省设置,后面将详细讨论。

    函数的第三个参数hints是addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索。函数的返回值是一个指向addrinfo结构的链表指针res。

    2addrinfo结构

     

    结构

    固定的参数

    typedef struct addrinfo {  

    int ai_flags;  

    int ai_family;  

    int ai_socktype;  

    int ai_protocol;  

    size_t ai_addrlen;  

    char* ai_canonname;  

    struct sockaddr* ai_addr;  

    struct addrinfo* ai_next;

    }

    ai_addrlen must be zero or a null pointer

    ai_canonname must be zero or a null pointer

    ai_addr must be zero or a null pointer

    ai_next must be zero or a null pointer

     

    可以改动的参数

     

    ai_flags:AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST

    ai_family: AF_INET,AF_INET6

    ai_socktype:SOCK_STREAM,SOCK_DGRAM

    ai_protocol:IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc.

    参数说明

    在getaddrinfo函数之前通常需要对以下6个参数进行以下设置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol

    在6项参数中,对函数影响最大的是nodename,sername和hints.ai_flag

    而ai_family只是有地址为v4地址或v6地址的区别。而ai_protocol一般是为0不作改动。

    其中ai_flags、ai_family、ai_socktype说明如下:

     

    参数

    取值

    说明

    ai_family

    AF_INET

    2

    IPv4

     

    AF_INET6

    23

    IPv6

     

    AF_UNSPEC

    0

    协议无关

    ai_protocol

    IPPROTO_IP

    0

    IP协议

     

    IPPROTO_IPV4

    4

    IPv4

     

    IPPROTO_IPV6

    41

    IPv6

     

    IPPROTO_UDP

    17

    UDP

     

    IPPROTO_TCP

    6

    TCP

    ai_socktype

    SOCK_STREAM

    1

     

    SOCK_DGRAM

    2

    数据报

    ai_flags

    AI_PASSIVE

    1

    被动的,用于bind,通常用于server socket

     

    AI_CANONNAME

    2

     

     

    AI_NUMERICHOST

    4

    地址为数字串

    对于ai_flags值的说明:

     

    AI_NUMERICHOST

    AI_CANONNAME

    AI_PASSIVE

    0/1

    0/1

    0/1

    如上表所示,ai_flagsde值范围为0~7,取决于程序如何设置3个标志位,比如设置ai_flags为 “AI_PASSIVE|AI_CANONNAME”,ai_flags值就为3。三个参数的含义分别为:

    (1)AI_PASSIVE当此标志置位时,表示调用者将在bind()函数调用中使用返回的地址结构。当此标志不置位时,表示将在connect()函数调用中使用。

    当节点名位NULL,且此标志置位,则返回的地址将是通配地址。

    如果节点名NULL,且此标志不置位,则返回的地址将是回环地址。

    (2)AI_CANNONAME当此标志置位时,在函数所返回的第一个addrinfo结构中的ai_cannoname成员中,应该包含一个以空字符结尾的字符串,字符串的内容是节点名的正规名。

    (3)AI_NUMERICHOST当此标志置位时,此标志表示调用中的节点名必须是一个数字地址字符串。

    4.实际使用的几种常用设置

    一般情况下,client/server编程中,server端调用bind(如果面向连接的还需要listen),client则不用掉bind函数,解析地址后直接connect(面向连接)或直接发送数据(无连接)。因此,比较常见的情况有

    (1)        通常服务器端在调用getaddrinfo之前,ai_flags设置AI_PASSIVE,用于bind;主机名nodename通常会设置为NULL,返回通配地址[::]。

    (2)        客户端调用getaddrinfo时,ai_flags一般不设置AI_PASSIVE,但是主机名nodename和服务名servname(更愿意称之为端口)则应该不为空。

    (3)        当然,即使不设置AI_PASSIVE,取出的地址也并非不可以被bind,很多程序中ai_flags直接设置为0,即3个标志位都不设置,这种情况下只要hostname和servname设置的没有问题就可以正确bind。

    上述情况只是简单的client/server中的使用,但实际在使用getaddrinfo和参考国外开源代码的时候,曾遇到一些将servname(即端口)设为NULL的情况

    (当然,此时nodename必不为NULL,否则调用getaddrinfo会报错)。以下分情况进行了测试:

    (1)        如果nodename是字符串型的IPv6地址,bind的时候会分配临时端口;

    (2)        如果nodename是本机名,servname为NULL,则根据操作系统的不同略有不同

    注意点是: 这个函数,说起来,是get ,但是其实可以理解为creat 或者是理解为构建 。 因为你可以随意构建自己的地址结构addrinfo。

    如 果本函数返回成功,那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串联起来的addrinfo结构链表。可以 导致返回多个addrinfo结构的情形有以下2个:

        1.    如果与hostname参数关联的地址有多个,那么适用于所请求地址簇的每个地址都返回一个对应的结构。

        2.    如果service参数指定的服务支持多个套接口类型,那么每个套接口类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype 成员。

     

    我们必须先分配一个hints结构,把它清零后填写需要的字段,再调用getaddrinfo然后遍历一个链表逐个尝试每个返回地 址。

     

    getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题。

     

    其中,如果getaddrinfo出 错,那么返回一个非0的错误值。

    官方说明的链接:http://www.kernel.org/doc/man-pages/online/pages/man3/gai_strerror.3.html

    很详细。

     

     

原创粉丝点击