• UNP学习笔记(第十一章 名字与地址转换)


    域名系统

    域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射。

    主机名既可以是一个简单得名字,如solaris,也可以是一个全限定域名,如solaris.unpbook.com。

    资源记录

    DNS中的条目称为资源记录(resource record,RR)

    A记录把一个主机名映射成一个32位的IPv4的地址。

    AAAA记录把一个主机名映射成一个128位的IPv6地址。

    PTR记录把IP地址映射成主机名。

    MX记录把一个主机指定作为主机的“邮件交换器(mail exchanger)”。

    CNAME代表“cononical name”(规范名字),它的常见用法为常用的服务指派CNAME记录,例如下面名为linux的主机有以下2个CNAME记录

    解析器和名字服务器

    每个组织机构往往运行一个或多个名字服务器,它们通常是所谓的BIND(Berkeley Internet Name Domain)程序。

    本书我们编写的应用程序通过调用成为解析器的函数库中的函数接触DNS服务器,常见的解析器函数是gethostbyname和gethostbyaddr。

    下图展示了应用程序、解析器和名字服务器的一个典型关系

    gethostbyname函数

    查找主机名最基本的函数是gethostbyname。如果调用成功,它就返回一个指向hostent结构的指针。

    #inlcude <netdb.h>
    struct hostent *gethostbyname(const char *hostname);
    
     struct hostent {
            char  *h_name;            /* official (canonical) name of host */
            char **h_aliases;         /* alias list */
            int    h_addrtype;        /* host address type */
            int    h_length;          /* length of address */
            char **h_addr_list;       /* list of addresses */
    };

    gethostbyname执行的是对A记录的查询。它只能返回IPv4地址

    下图展示了hostent结构和它包含的信息

    下面给出调用gethostbyname的简单例子

     1 #include    "unp.h"
     2 
     3 int
     4 main(int argc, char **argv)
     5 {
     6     char            *ptr, **pptr;
     7     char            str[INET_ADDRSTRLEN];
     8     struct hostent    *hptr;
     9 
    10     while (--argc > 0) {
    11         ptr = *++argv;
    12         if ( (hptr = gethostbyname(ptr)) == NULL) {
    13             err_msg("gethostbyname error for host: %s: %s",
    14                     ptr, hstrerror(h_errno));
    15             continue;
    16         }
    17         printf("official hostname: %s
    ", hptr->h_name);
    18 
    19         for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)
    20             printf("	alias: %s
    ", *pptr);
    21 
    22         switch (hptr->h_addrtype) {
    23         case AF_INET:
    24             pptr = hptr->h_addr_list;
    25             for ( ; *pptr != NULL; pptr++)
    26                 printf("	address: %s
    ",
    27                     Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
    28             break;
    29 
    30         default:
    31             err_ret("unknown address type");
    32             break;
    33         }
    34     }
    35     exit(0);
    36 }
    View Code

    运行结果

    gethostbyaddr函数

    与gethostbyname的行为相反,gethostbyaddr函数试图由一个二进制的IP地址找到相应的主机名

    #include <netdb.h>
    struct hostent *gethostbyaddr(const char *addr,socklen_t len,int family);
                           //返回:若成功则为非空指针,若出错则为NULL且设置h_errno

    addr参数实际上不是char *类型,而是一个指向存放IPv4地址的某个in_addr结构的指针

    gethostbyaddr向一个名字服务器查询PTR记录。我们感兴趣的字段通常是规范主机名的h_name。

    getservbyname和getservbyport函数

    getservbyname函数用于根据给定名字查找相应服务

    名字到端口号的映射关系保存在一个文件中(通常是/etc/services)

    #include <netdb.h>
    struct servent *getservbyname(const char *servername,const char *protoname);

    servent结构如下

    struct servent {
            char  *s_name;       /* official service name */
            char **s_aliases;    /* alias list */
            int    s_port;       /* port number */
            char  *s_proto;      /* protocol to use */
    };

    servent结构中我们关心的主要字段是端口号,下面是本函数的典型调用

    struct servent *sptr;
    
    sptr=getservbyname("domain","unp");   /* DNS using UNP */
    sptr=getservbyname("ftp","tcp");      /* FTP using TCP*/
    sptr=getservbyname("ftp",NULL);       /* FTP using TCP*/
    sptr=getservbyname("ftp","unp");      /* this call will fail */

    getservbyport用于根据给定端口号和可选协议查找相应服务

    #include <netdb.h>
    struct servent *getservbyport(int port,const char *protoname);

    port参数的值必须网络字节序。本函数的典型调用如下

    struct servent *sptr;
    
    sptr=getservbyport(htons(53),"unp");  /* DNS using UDP */
    sptr=getservbyport(htons(21),"tcp");  /* FTP using TCP*/
    sptr=getservbyport(htons(21),NULL);  /* FTP using TCP*/
    sptr=getservbyport(htons(21),"unp");  /* this call will fail */

    有些端口号在TCP上用于一种服务,在UDP上却用于完全不同的另一种服务

    下面是使用gethostbyname和getservbyname的时间获取客户程序

     1 #include    "unp.h"
     2 
     3 int
     4 main(int argc, char **argv)
     5 {
     6     int                    sockfd, n;
     7     char                recvline[MAXLINE + 1];
     8     struct sockaddr_in    servaddr;
     9     struct in_addr        **pptr;
    10     struct in_addr        *inetaddrp[2];
    11     struct in_addr        inetaddr;
    12     struct hostent        *hp;
    13     struct servent        *sp;
    14 
    15     if (argc != 3)
    16         err_quit("usage: daytimetcpcli1 <hostname> <service>");
    17 
    18     if ( (hp = gethostbyname(argv[1])) == NULL) {
    19         if (inet_aton(argv[1], &inetaddr) == 0) {
    20             err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));
    21         } else {
    22             inetaddrp[0] = &inetaddr;
    23             inetaddrp[1] = NULL;
    24             pptr = inetaddrp;
    25         }
    26     } else {
    27         pptr = (struct in_addr **) hp->h_addr_list;
    28     }
    29 
    30     if ( (sp = getservbyname(argv[2], "tcp")) == NULL)
    31         err_quit("getservbyname error for %s", argv[2]);
    32 
    33     for ( ; *pptr != NULL; pptr++) {
    34         sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    35 
    36         bzero(&servaddr, sizeof(servaddr));
    37         servaddr.sin_family = AF_INET;
    38         servaddr.sin_port = sp->s_port;
    39         memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
    40         printf("trying %s
    ",
    41                Sock_ntop((SA *) &servaddr, sizeof(servaddr)));
    42 
    43         if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0)
    44             break;        /* success */
    45         err_ret("connect error");
    46         close(sockfd);
    47     }
    48     if (*pptr == NULL)
    49         err_quit("unable to connect");
    50 
    51     while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
    52         recvline[n] = 0;    /* null terminate */
    53         Fputs(recvline, stdout);
    54     }
    55     exit(0);
    56 }
    View Code

    getaddrinfo函数

    getaddrinfo函数能够处理名字到地址以及服务到端口的这两种转换,而且支持IPv6。

    #include <netdb.h>
    int getaddrinfo(const char *hostname,
                    const char *service,
                    const struct addrinfo *hint,
                    struct addrinfo **result);

    本函数通过result指针参数返回一个指向addrinfo结构链表的指针,该结构定义如下

    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;
    };

    hostname参数是一个主机名或地址串(IPv4点分十进制数串或IPv6十六进制数串)

    service参数是一个服务名或十进制端口号数串

    hints参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。

    例如,如果指定的服务既支持TCP也支持UDP,那么调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM,使得返回的仅仅是适用于数据报套接字的信息。

    hints结构中调用者可以设置的成员有:

    1.ai_flags(0个或多个或在一起的AI_xxx值)

    2.ai_family(某个AF_xxx值)

    3.ai_socktype(某个SOCK_xxx值)

    4.ai_protocol

    ai_flags成员可用的标志值及含义如下:

    AI_PASSIVE             套接字将用于被动打开

    AI_CONONNAME       告诉getaddrinfo函数返回主机的规范名字

    AI_NUMERICHOST    防止任何类型的名字到地址映射,hostname参数必须是一个地址串

    AI_NUMERICSERV     防止任何类型的名字到服务器映射,service参数必须是一个十进制端口号数串

    AI_V4MAPPED           如果同时制定ai_family成员为AF_INET6,那么如果没有可用的AAAA记录,就返回A记录对应的IPv4映射的IPv6地址

    AI_ALL                     如果同时指定AI_V4MAPPED标志,那么除了返回与AAAA记录对应的IPv6地址外,还返回与A记录对应的IPv4映射的IPv6地址

    AI_ADDRCONFIG       按照所在主机的配置选择返回地址类型

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

    1.如果与hostname参数关联的地址有多个,则每个地址都返回一个对应的结构

    2.如果service参数指定的服务支持多个套接字类型,那么每个套接字类型都可能返回一个对应的结构

    在addrinfo结构中返回的信息可现成用于socket调用,随后现成用于客户的connect或sendto调用,或者社和服务器的bind调用。

    如:socket函数的参数就是addrinfo结构中的ai_family、ai_socktype和ad_addr成员。

    下图是getaddrinfo返回信息的实例

     下面查看这个函数的一些常见的输入:

    1.指定hostname和service。这是TCP或UDP客户进程调用getaddrinfo的常规输入。

       该调用返回后,TCP客户在一个循环中针对每个返回的IP地址,逐一调用socket和connect,直到有一个连接成功,或者所有地址尝试完毕为止。

    2.典型的服务器值指定service而不指定hostname,同时在hints结构中指定AI_PASSIVE标志。返回的套接字地址结构中应含有一个值为INADDR_ANY(对于IPv4)的IP地址

    3.服务器可以使用select或poll函数让服务器进程处理多个套接字。

       这种情况下,服务器将遍历getaddrinfo返回的整个addrinfo结构链表,并为每个结构创建一个套接字,再使用select或poll。

    gai_strerror函数

    下图个出可由getaddrinfo返回的非0错误值的名字和含义。gai_strerror以这些值为它的唯一参数,返回对应的出错字符串

    #include <netdb.h>
    const char *gai_strerror(int error);

    freeaddrinfo函数

    由getaddrinfo返回的所有存储空间都是动态分配的。这些存储空间通过调用freeaddrinfo释放

    #include <netdb.h>
    void freeaddrinfo(struct addrinfo *ai);

    下面函数是使用getaddrinfo的接口函数

    host_serv函数

    host_serv函数不要求调用者分配并填写一个hints结构,该结构中我们感兴趣的两个字段(地址族和套接字类型)成为这个接口函数的参数。

    #include    "unp.h"
    struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype);
    //返回:若成功则为指向addrinfo结构的指针,若出错则为NULL

    下面是该函数的源代码

     1 struct addrinfo *
     2 host_serv(const char *host, const char *serv, int family, int socktype)
     3 {
     4     int                n;
     5     struct addrinfo    hints, *res;
     6 
     7     bzero(&hints, sizeof(struct addrinfo));
     8     hints.ai_flags = AI_CANONNAME;    /* always return canonical name */
     9     hints.ai_family = family;        /* AF_UNSPEC, AF_INET, AF_INET6, etc. */
    10     hints.ai_socktype = socktype;    /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
    11 
    12     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    13         return(NULL);
    14 
    15     return(res);    /* return pointer to first on linked list */
    16 }
    View Code

    tcp_connect函数

    我们使用getaddrinfo编写tcp_connect函数:创建一个TCP套接字并连接到一个服务器。

    #include    "unp.h"
    int tcp_connect(const char *host, const char *serv);

    下面是该函数的源代码

     1 #include    "unp.h"
     2 
     3 int
     4 tcp_connect(const char *host, const char *serv)
     5 {
     6     int                sockfd, n;
     7     struct addrinfo    hints, *res, *ressave;
     8 
     9     bzero(&hints, sizeof(struct addrinfo));
    10     hints.ai_family = AF_UNSPEC;
    11     hints.ai_socktype = SOCK_STREAM;
    12 
    13     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    14         err_quit("tcp_connect error for %s, %s: %s",
    15                  host, serv, gai_strerror(n));
    16     ressave = res;
    17 
    18     do {
    19         sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    20         if (sockfd < 0)
    21             continue;    /* ignore this one */
    22 
    23         if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
    24             break;        /* success */
    25 
    26         Close(sockfd);    /* ignore this one */
    27     } while ( (res = res->ai_next) != NULL);
    28 
    29     if (res == NULL)    /* errno set from final connect() */
    30         err_sys("tcp_connect error for %s, %s", host, serv);
    31 
    32     freeaddrinfo(ressave);
    33 
    34     return(sockfd);
    35 }
    View Code

    使用tcp_connect重新编写时间获取客户程序

     1 #include    "unp.h"
     2 
     3 int
     4 main(int argc, char **argv)
     5 {
     6     int                sockfd, n;
     7     char            recvline[MAXLINE + 1];
     8     socklen_t        len;
     9     struct sockaddr_storage    ss;
    10 
    11     if (argc != 3)
    12         err_quit("usage: daytimetcpcli <hostname/IPaddress> <service/port#>");
    13 
    14     sockfd = Tcp_connect(argv[1], argv[2]);
    15 
    16     len = sizeof(ss);
    17     Getpeername(sockfd, (SA *)&ss, &len);
    18     printf("connected to %s
    ", Sock_ntop_host((SA *)&ss, len));
    19 
    20     while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
    21         recvline[n] = 0;    /* null terminate */
    22         Fputs(recvline, stdout);
    23     }
    24     exit(0);
    25 }
    View Code

    tcp_listen函数

    tcp_listen执行TCP服务器的通常步骤:创建一个TCP套接字,给它捆绑服务器的总所周知的端口,并允许接收外来的连接请求。

    #include    "unp.h"
    int tcp_listen(const char *host, const char *serv, socklen_t *addrlenp);
                                //返回:若成功则为连接套接字描述符,若出错则不返回

    下面是该函数的源代码

     1 #include    "unp.h"
     2 
     3 int
     4 tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
     5 {
     6     int                listenfd, n;
     7     const int        on = 1;
     8     struct addrinfo    hints, *res, *ressave;
     9 
    10     bzero(&hints, sizeof(struct addrinfo));
    11     hints.ai_flags = AI_PASSIVE;
    12     hints.ai_family = AF_UNSPEC;
    13     hints.ai_socktype = SOCK_STREAM;
    14 
    15     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    16         err_quit("tcp_listen error for %s, %s: %s",
    17                  host, serv, gai_strerror(n));
    18     ressave = res;
    19 
    20     do {
    21         listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    22         if (listenfd < 0)
    23             continue;        /* error, try next one */
    24 
    25         Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    26         if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
    27             break;            /* success */
    28 
    29         Close(listenfd);    /* bind error, close and try next one */
    30     } while ( (res = res->ai_next) != NULL);
    31 
    32     if (res == NULL)    /* errno from final socket() or bind() */
    33         err_sys("tcp_listen error for %s, %s", host, serv);
    34 
    35     Listen(listenfd, LISTENQ);
    36 
    37     if (addrlenp)
    38         *addrlenp = res->ai_addrlen;    /* return size of protocol address */
    39 
    40     freeaddrinfo(ressave);
    41 
    42     return(listenfd);
    43 }
    View Code

    下面使用tcp_listen重新编写时间获取服务器程序

     1 #include    "unp.h"
     2 #include    <time.h>
     3 
     4 int
     5 main(int argc, char **argv)
     6 {
     7     int                listenfd, connfd;
     8     socklen_t        len;
     9     char            buff[MAXLINE];
    10     time_t            ticks;
    11     struct sockaddr_storage    cliaddr;
    12 
    13     if (argc != 2)
    14         err_quit("usage: daytimetcpsrv1 <service or port#>");
    15 
    16     listenfd = Tcp_listen(NULL, argv[1], NULL);
    17 
    18     for ( ; ; ) {
    19         len = sizeof(cliaddr);
    20         connfd = Accept(listenfd, (SA *)&cliaddr, &len);
    21         printf("connection from %s
    ", Sock_ntop((SA *)&cliaddr, len));
    22 
    23         ticks = time(NULL);
    24         snprintf(buff, sizeof(buff), "%.24s
    ", ctime(&ticks));
    25         Write(connfd, buff, strlen(buff));
    26 
    27         Close(connfd);
    28     }
    29 }
    View Code

    下面是tcp_listen的协议无关时间获取服务器程序

     1 #include    "unp.h"
     2 #include    <time.h>
     3 
     4 int
     5 main(int argc, char **argv)
     6 {
     7     int                listenfd, connfd;
     8     socklen_t        len, addrlen;
     9     char            buff[MAXLINE];
    10     time_t            ticks;
    11     struct sockaddr_storage    cliaddr;
    12 
    13     if (argc == 2)
    14         listenfd = Tcp_listen(NULL, argv[1], &addrlen);
    15     else if (argc == 3)
    16         listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
    17     else
    18         err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>");
    19 
    20     for ( ; ; ) {
    21         len = sizeof(cliaddr);
    22         connfd = Accept(listenfd, (SA *)&cliaddr, &len);
    23         printf("connection from %s
    ", Sock_ntop((SA *)&cliaddr, len));
    24 
    25         ticks = time(NULL);
    26         snprintf(buff, sizeof(buff), "%.24s
    ", ctime(&ticks));
    27         Write(connfd, buff, strlen(buff));
    28 
    29         Close(connfd);
    30     }
    31 }
    View Code

    udp_client函数

    使用getaddrinfo来编写udp_client函数。

    本函数创建一个为连接的UDP套接字,并返回3项数据。

    1.返回值是该套接字的描述符

    2.把目的IP地址和端口存放在saptr指向的套接字地址结构中,用于稍后调用sendto

    3.这个套接字地址结构的大小在lenp指向的变量中返回

    #include    "unp.h"
    int udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp);
                                       //返回:若成功则为未连接套接字描述符,若出错则不返回

    下面是该函数的源代码

     1 #include    "unp.h"
     2 
     3 int
     4 udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp)
     5 {
     6     int                sockfd, n;
     7     struct addrinfo    hints, *res, *ressave;
     8 
     9     bzero(&hints, sizeof(struct addrinfo));
    10     hints.ai_family = AF_UNSPEC;
    11     hints.ai_socktype = SOCK_DGRAM;
    12 
    13     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    14         err_quit("udp_client error for %s, %s: %s",
    15                  host, serv, gai_strerror(n));
    16     ressave = res;
    17 
    18     do {
    19         sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    20         if (sockfd >= 0)
    21             break;        /* success */
    22     } while ( (res = res->ai_next) != NULL);
    23 
    24     if (res == NULL)    /* errno set from final socket() */
    25         err_sys("udp_client error for %s, %s", host, serv);
    26 
    27     *saptr = Malloc(res->ai_addrlen);
    28     memcpy(*saptr, res->ai_addr, res->ai_addrlen);
    29     *lenp = res->ai_addrlen;
    30 
    31     freeaddrinfo(ressave);
    32 
    33     return(sockfd);
    34 }
    View Code

    协议无关时间获取客户程序

     1 #include    "unp.h"
     2 
     3 int
     4 main(int argc, char **argv)
     5 {
     6     int                sockfd, n;
     7     char            recvline[MAXLINE + 1];
     8     socklen_t        salen;
     9     struct sockaddr    *sa;
    10 
    11     if (argc != 3)
    12         err_quit("usage: daytimeudpcli1 <hostname/IPaddress> <service/port#>");
    13 
    14     sockfd = Udp_client(argv[1], argv[2], (void **) &sa, &salen);
    15 
    16     printf("sending to %s
    ", Sock_ntop_host(sa, salen));
    17 
    18     Sendto(sockfd, "", 1, 0, sa, salen);    /* send 1-byte datagram */
    19 
    20     n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
    21     recvline[n] = '';    /* null terminate */
    22     Fputs(recvline, stdout);
    23 
    24     exit(0);
    25 }
    View Code

    udp_connect函数

    udp_connect函数创建一个已连接UDP套接字

    #include    "unp.h"
    int udp_connect(const char *host, const char *serv);
              //若成功则为已连接套接字描述符,若出错则不返回

    下面是该函数的源代码

     1 #include    "unp.h"
     2 
     3 int
     4 udp_connect(const char *host, const char *serv)
     5 {
     6     int                sockfd, n;
     7     struct addrinfo    hints, *res, *ressave;
     8 
     9     bzero(&hints, sizeof(struct addrinfo));
    10     hints.ai_family = AF_UNSPEC;
    11     hints.ai_socktype = SOCK_DGRAM;
    12 
    13     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    14         err_quit("udp_connect error for %s, %s: %s",
    15                  host, serv, gai_strerror(n));
    16     ressave = res;
    17 
    18     do {
    19         sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    20         if (sockfd < 0)
    21             continue;    /* ignore this one */
    22 
    23         if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
    24             break;        /* success */
    25 
    26         Close(sockfd);    /* ignore this one */
    27     } while ( (res = res->ai_next) != NULL);
    28 
    29     if (res == NULL)    /* errno set from final connect() */
    30         err_sys("udp_connect error for %s, %s", host, serv);
    31 
    32     freeaddrinfo(ressave);
    33 
    34     return(sockfd);
    35 }
    View Code

    udp_server函数

    本函数的参数与tcp_listen一样,有一个可选的hostname和一个必须的service(从而可以绑定其端口号),以及一个可选的指向某个变量的指针,用于返回套接字地址结构的大小

    #include    "unp.h"
    int udp_server(const char *host, const char *serv, socklen_t *addrlenp);
                              //返回:若成功则为未连接套接字描述符,若出错则不返回

    下面是该函数的源代码

     1 #include    "unp.h"
     2 
     3 int
     4 udp_server(const char *host, const char *serv, socklen_t *addrlenp)
     5 {
     6     int                sockfd, n;
     7     struct addrinfo    hints, *res, *ressave;
     8 
     9     bzero(&hints, sizeof(struct addrinfo));
    10     hints.ai_flags = AI_PASSIVE;
    11     hints.ai_family = AF_UNSPEC;
    12     hints.ai_socktype = SOCK_DGRAM;
    13 
    14     if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
    15         err_quit("udp_server error for %s, %s: %s",
    16                  host, serv, gai_strerror(n));
    17     ressave = res;
    18 
    19     do {
    20         sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    21         if (sockfd < 0)
    22             continue;        /* error - try next one */
    23 
    24         if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
    25             break;            /* success */
    26 
    27         Close(sockfd);        /* bind error - close and try next one */
    28     } while ( (res = res->ai_next) != NULL);
    29 
    30     if (res == NULL)    /* errno from final socket() or bind() */
    31         err_sys("udp_server error for %s, %s", host, serv);
    32 
    33     if (addrlenp)
    34         *addrlenp = res->ai_addrlen;    /* return size of protocol address */
    35 
    36     freeaddrinfo(ressave);
    37 
    38     return(sockfd);
    39 }
    View Code

    协议无关时间获取服务器程序

     1 #include    "unp.h"
     2 #include    <time.h>
     3 
     4 int
     5 main(int argc, char **argv)
     6 {
     7     int                sockfd;
     8     ssize_t            n;
     9     char            buff[MAXLINE];
    10     time_t            ticks;
    11     socklen_t        len;
    12     struct sockaddr_storage    cliaddr;
    13 
    14     if (argc == 2)
    15         sockfd = Udp_server(NULL, argv[1], NULL);
    16     else if (argc == 3)
    17         sockfd = Udp_server(argv[1], argv[2], NULL);
    18     else
    19         err_quit("usage: daytimeudpsrv [ <host> ] <service or port>");
    20 
    21     for ( ; ; ) {
    22         len = sizeof(cliaddr);
    23         n = Recvfrom(sockfd, buff, MAXLINE, 0, (SA *)&cliaddr, &len);
    24         printf("datagram from %s
    ", Sock_ntop((SA *)&cliaddr, len));
    25 
    26         ticks = time(NULL);
    27         snprintf(buff, sizeof(buff), "%.24s
    ", ctime(&ticks));
    28         Sendto(sockfd, buff, strlen(buff), 0, (SA *)&cliaddr, len);
    29     }
    30 }
    View Code

    getnameinfo函数

    getnameinfo是getaddrinfo的互补函数,它以一个套接字地址为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。

    本函数以协议无关的方式提供这些信息。

    #include <netdb.h>
    int getnameinfo(const struct sockaddr *sockaddr,socklen_t addrlen,
                    char *host,socklen_t hostlen,
                    char *serv,socklen_t servlen,int flag);

    flags参数是下面6个可指定的标志

    可重入函数

    gethostbyname函数不是一个可重入的函数。

    关于可重入函数可以查看 http://www.cnblogs.com/runnyu/p/4643764.html

    gethostbyname_r和gethostbyaddr_r函数提供了对原来两个函数的可重入版本

  • 相关阅读:
    Leecode no.22 括号生成
    修改mysql数据库的时区
    Django 路由层之反向解析
    学习 Django 的几个教程网址
    leetcode周赛 242
    AcWing第二次热身赛
    AcWing夏季每日一题--最长公共子序列
    AcWIng夏季每日一题--序列最大收益
    leetcode周赛 241
    第十二届蓝桥杯C++ B组
  • 原文地址:https://www.cnblogs.com/runnyu/p/4663514.html
Copyright © 2020-2023  润新知