11.1. 概述

函数gethostbyname和gethostbyaddr是依赖于协议的,使用前一个函数时,我们必须知道放置结果的套接口地址结构的成员是哪一种(举例来说,IPv4使用sin_addr成员,IPv6使用sin6_addr成员),而调用后一个函数时,必须知道存放二进制地址的是哪一种成员。

getaddrinfo和getnameinfo能为应用程序提供协议独立性。

11.2. getaddrinfo函数

#include <netdb.h>int getaddrinfo(const char * hostname, const char * service, const struct addrinfo * hints, struct addrinfo * * result); //返回:成功返回0,出错返回非零

这个函数通过result指针返回一个指向addrinfo结构链表的指针,该结构在<netdb.h>中定义:

struct addrinfo{int ai_flags; /* AI_PASSIVE, AI_CANONNAME */int ai_family; /* AF_xxx */int ai_socktype; /* SOCK_xxx */int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */  size_t ai_addrlen; /* length of ai_addr */char * ai_canonname; /* ptr to canonical name for host */struct sockaddr * ai_addr; /* ptr to socket address structure */struct addrinfo * ai_next; /* ptr to next structure in linked list */};

其中hostname是主机名或地址串,service是服务器或十进制数的端口号字符串,hints是一个空指针或指向一个addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索。

11.3. gai_strerror函数

#include <netdb.h>char * gai_strerror(int error); //返回: 一个指向描述出错信息字符串的指针

11.4. freeaddrinfo函数

由getaddrinfo返回的存储空间,包括addrinfo结构、ai_addr结构、ai_canonname字符串,都是用malloc动态获取的。这些空间可调用freeaddrinfo释放。

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

ai应指向getaddrinfo返回的第一个addrinfo结构。在该链表中的所有结构,以及这些机构所指向的动态存储空间都将被释放。

只复制addrinfo结构,而不复制addrinfo结构所指向的其他结构,叫做浅拷贝或浅复制(shallow copy)。复制addrinfo结构,同时复制addrinfo结构所指向的其他结构,称为深拷贝或深复制(deep copy)。

11.5. getaddrinfo函数:IPv6和UNIX域

11.6. getaddrinfo函数:例子

11.7. host_serv函数

我们给getaddrinfo设计的第一个接口不需要调用者来分配和填写hints结构。这个host_serv函数以地址族和套接口类型作为参数:

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

函数源代码:

#include "unp"struct addrinfo * host_serv(const char * host, const char * serv, int family, int socktype){int n;struct addrinfo hints, *res;  bzero(&hints, sizeof(sturct addrinfo));  hints.ai_flags = AI_CANONNAME; /* always return canonical name */  hints.ai_family = family; /* AF_UNSPEC, AF_INET, AF_INET6, etc. */  hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */if((n=getaddrinfo(host, serv, &hints, &res)) != 0)return(NULL);return(res); /* return pointer to first on linked list */}

11.8. tcp_connect函数

现在我们编写两个使用getaddrinfo的函数,以处理我们编写的TCP客户和服务器程序的大部分情况。第一个函数即tcp_connect执行客户程序的一般步骤:创建一个TCP套接口并与服务器建立连接。

#include "unp.h"int tcp_connect(const char * hostname, const char * service); //返回: 如成功则返回已连接套接口的描述字,出错则不返回

它的源代码如下:

#include "unp.h"int tcp_connect(const char * host, const char * serv){int sockfd, n;struct addrinfo hints, * res, * ressave;  bzero(&hints, sizeof(struct addrinfo));  hints.ai_family = AF_UNSPEC;  hints.ai_socktype = SOCK_STREAM;if((n = getaddrinfo(host, serv, &hints, &res)) != 0)    err_quit("tcp_connect error for %s, %s: %s", host, serv, gai_strerror(n));  ressave = res;do{    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if(sockfd < 0)continue; /* ignore this one */if(connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)break; /* success */    Close(sockfd); /*ignore this one */  } while( (res = res->ai_next) != NULL);if(res == NULL) /* errno set from final connect() */    err_sys("tcp_connect error for %s, %s", host, serv);  freeaddrinfo(ressave);return(sockfd);}

用tcp_connect重新编码的时间日期客户程序

#include "unp.h"int main(int argc, char * * argv){int sockfd, n;char recvline[MAXLINE+1];  socklen_t len;struct sockaddr * sa;if(argc != 3)    err_quit("usage: daytimectpcli <hostname/IPaddress> <service/port#>");  sockfd = Tcp_connect(argv[1],argv[2]);  sa = Malloc(MAXSOCKADDR);  len = MAXSOCKADDR;  Getpeername(sockfd, sa, &len);  printf("connect to %s \n", Sock_ntop_host(sa,len));while((n = Read(sockfd, recvline,MAXLINE)) > 0)  {    recvline[n] = 0; /* null terminate */    Fputs(recvline,stdout);  }  exit(0);}

11.9. tcp_listen函数

下一个函数即tcp_listen执行TCP服务器程序的一般步骤:创建一个TCP套接口,给它捆绑服务器的众所周知端口,并允许接受外来的连接请求。

#include "unp.h"int tcp_listen(const char * hostname, const char * service, socklen_t * lenptr); //返回: 成功返回已连接套接口描述字,出错则不返回

它的源代码如下:

#include "unp.h"int top_listen(const char * host, const char * serv, socklen_t * addrlenp){int listenfd, n;const int on = 1;struct addrinfo hints, * res, * ressave;  bzero(&hints, sizeof(struct addrinfo));  hints.ai_flags = AI_PASSIVE;  hints.ai_family = AF_UNSPEC;  hints.ai_socktype = SOCK_STREAM;if( (n = getaddrinfo(host, serv, &hints, &res)) !=0 )    err_quit("tcp_listen error for %s, %s: %s", host, serv, gai_strerror(n));  ressave = res;do{    listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if(listen<0)continue; /* error, try next one */    Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));if(bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)break; /* success */    Close(listenfd); /* bind error, close and try next one */  } while( (res = res->ai_next) != NULL);if(res == NULL) /* errno from final socket() or bind() */    err_sys("tcp_listen error for %s, %s", host ,serv);  Listen(listenfd, LISTENQ);if(addrlenp)    * addrlenp = res->ai_addrlen; /*return size of protocol address */  freeaddrinfo(ressave);return(listenfd);}

用tcp_listen重新编码的时间日期服务器程序

#include "unp.h"#include <time.h>int main(int argc, char * * argv){int listenfd, connfd;  socklen_t addrlen, len;char buff[MAXLINE];  time_t ticks;struct sockaddr * cliaddr;if(argc != 2)    err_quit("usage: daytimetcpsrv1 <service or port#>");  listenfd = Tcp_listen(NULL, argv[1], &addrlen);  cliaddr = Malloc(addrlen);for( ; ; )  {    len = addrlen;    connfd = Accept(listenfd, cliaddr, &len);    printf("connection from %s \n", Sock_ntop(cliaddr, len));    ticks = time(NULL);    sprintf(buff, sizeof(buff), "%.24s \r \n", ctime(&ticks));    Write(connfd, buff, strlen(buff));    Close(connfd);  }}

11.10. udp_client函数

给getaddrinfo提供简单接口的函数在UDP这儿有所改变,因为这里我们提供了一个创建未连接UDP套接口的客户函数,在下一节中则提供另一个创建已连接UDP套接口的函数

#include "unp.h"int udp_client(const char * hostname, const char * service, void * * saptr, socklen_t * lenp); // 返回: 成功返回未连接套接口的描述字,出错则不返回

这个函数创建一个未连接UDP套接口,返回三项数据。第一,返回值是套接口描述字。第二,saptr是一个指向套接口地址结构(由udp_client动态分配)的指针(由调用者生命)的地址,在这个结构中存放目的IP地址和端口号,用来调用sendto,套接口地址结构的大小在lenp指向的变量中返回。最后一个参数不能是空指针(tcp_listen的最后一个参数是允许的),因为套接口地址结构的长度在调用sendto和recvfrom时都是需要的。

下面是它的源代码:getaddrinfo转换hostname和service参数,socket创建数据报套接口,malloc为一个套接口地址结构分配内存,并由memcpy将对应于创建的套接口的地址结构拷贝到这个内存区域中

#include "unp.h"int udp_client(const char * host, const char * serv, void * * saptr, socklen_t * lenp){int sockfd, n;struct addrinfo hints, * res, * ressave;  bzero(&hints, sizeof(struct addrinfo));  hints.ai_family = AF_UNSPEC;  hints.ai_socktype = SOCK_DGRAM;if( (n = getaddrinfo(host, serv, &hints, &res)) != 0)    err_quit("udp_client error for %s, %s: %s", host, serv, gai_strerror(n));  ressave = res;do{    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if(sockfd >= 0)break; /* success */  } while((res = res->ai_next) != NULL);if(res == NULL)  /* errno set from final socket() */    err_sys("udp_client error for %s, %s", host, serv);  * saptr = Malloc(res->ai_addrlen);  memcpy( * saptr, res->ai_addr, res->ai_addrlen);  * lenp = res->ai_addrlen;  freeaddrinfo(ressave);return(sockfd);}

使用UDP和udp_client函数重新编写独立于协议的程序的源代码

#include "unp.h"int main(int argc, char * * argv){int sockfd, n;char recvline[MAXLINE+1];  socklen_t salen;struct sockaddr * sa;if(argc != 3)    err_quit("usage: daytimeudpcli1 <hostname/IPaddress> <service/port>");  sockfd = Udp_client(argv[1], argv[2], (void * *)&sa, &salen);  printf("sending to %s \n", Sock_notp_host(sa, salen));  Sendto(sockfd, "", 1, 0, sa, salen); /* send 1-byte datagram */  n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);  recvline[n] = 0; /# null terminate */  Fputs(recvline, stdout);  exit(0);}

11.11. udp_connect函数

#include "unp.h"int udp_connect(const char * hostname, const char * service); //返回: 如果成功在返回套接口描述字,出错则不返回

它的源代码如下:

#include "unp.h"int udp_connect(const char * host, const char * serv){int sockfd, n;struct addrinfo hints, * res, * ressave;  bzero(&hints, sizeof(struct addrinfo));  hints.ai_family = AF_UNSPEC;  hints.ai_socktype = SOCK_DGRAM;if((n = getaddrinfo(host, serv, &hints, &res)) != 0)    err_quit("udp_connect error for %s, %s: %s", host, serv, gai_strerror(n));  ressave = res;do  {    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if(sockfd < 0)continue; /* ignore this one */if(connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)break; /* success */    Close(sockfd); /* ignore this one */  } while((res = res->ai_next) != NULL);if(res == NULL)  /* errno set from final connect() */    err_sys("udp_connect error for %s, %s", host, serv);  freeaddrinfo(ressave);return(sockfd);}

11.12. udp_server函数

我们为简化的getaddrinfo的接口开发的最后一个UDP函数是udp_server

#include "unp.h"int udp_server(const char * hostname, const char * service, socklen_t * lenptr); //返回:成功返回未连接套接口描述字,失败不返回

参数和tcp_listen一样:一个可选的hostname,一个必需的service(已给它捆绑一个端口)以及一个可选的指针,它指向返回套接口地址结构大小的变量。

#include "unp.h"int udp_server(const char * host, const char * serv, socklen_t * addrlenp){int sockfd,  n;struct addrinfo hints, * res, * ressave;  bzero(&hints, sizeof(struct addrinfo));  hints.ai_flags = AI_PASSIVE;  hints.ai_family = AF_UNSPEC;  hints.ai_stocktype = SOCK_DGRAM;if((n = getaddrinfo(host, serv, &hints, &res)) != 0)    err_quit("udp_server error for %s, %s: %s", host, serv, gai_strerror(n));  ressave = res;do{    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);if(sockfd < 0)continue; /* error, try next one */if(bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)break; /* success */    Close(sockfd); /* bind error, close and try next one */  } while((res = res->next) != NULL);if(res == NULL) /* error from final socket() or bind() */    err_sys("udp_server error fro %s, %s", host, serv);if(addrlenp)    * addrlenp = res->ai_addrlen; /* return size of protocol address */  freeaddrinfo(ressave);return(sockfd);}

11.13. getnameinfo函数

这个函数与getaddrinfo互补:它以一个套接口地址为参数,返回一个描述主机的字符串和一个描述服务的字符串。这个函数以一种独立于协议的方式提供这些信息

#include <netdb.h>int getnameinfo(const struct sockaddr * sockaddr, socklen_t addrlen, char * host, size_t hostlen, char * serv, size_t servlen, int flags); // 返回:成功返回0, 出错返回-1

sockaddr指向包含协议地址的套接口地址结构,它将会被转换成可读的字符串,addrlen是结构的长度。这个结构和长度通常由accept, recvfrom, getsockname,getpeername返回。

sock_ntop和getnameinfo的差别在于,前者不差DNS直接返回可输出的IP地址和端口号,而后者通常试图给主机和服务查到一个名字。

11.14. 可重入函数

线程

11.15. gethostbyname_r和gethostbyaddr_r函数

有两种方法可将像gethostbyname这样的不可重入函数变成可重入函数:

1. 变由函数填写并返回一个静态结果为:由调用者分配结构所需的空间,由可重入函数来填写

2. 可重入函数调用malloc动态分配内存

#include <netdb.h>struct hostent * gethostbyname_r(const char * hostname, struct hostent * result, char * buf, int buflen, int * h_errnop); // 返回: 成功返回非空指针,出错返回NULLstruct hostent * gethostbyaddr_r(const char * addr, int len, int type, struct hostent * result, char * buf, int buflen, int * h_errnop); // 返回: 成功返回非空指针,出错返回NULL

11.16. getaddrinfo和getnameinfo函数的实现

11.17 小结

转载于:https://www.cnblogs.com/s7vens/archive/2012/01/19/2327534.html

UNP Chapter 11 - 高级名字与地址转换相关推荐

  1. UNP Chapter 9 - 基本名字与地址转换

    9.1. 概述 本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在 ...

  2. Unix网络编程学习笔记之第11章 名字与地址转换

    一. 域名系统(DNS) 1. 简介 DNS主要用于主机名和IP地址之间的映射. 主机名可以是简单的名字ljm,也可以是全限定域名ljm.localdomainbaidu.com等. 2.资源记录 D ...

  3. 名字与地址转换getservbyname 与 getservbyport函数

    名字与地址转换getservbyname 与 getservbyport函数 服务也通常靠名字来标志,getservbyname函数用于根据给定名字查找相应服务. #include struct    ...

  4. UNP编程:29---名字与地址转换之(域名系统)

    一.域名系统的概念 域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射. 主机名既可以是一个简单名字(simple name),例如solaris或bsdi, ...

  5. UNP总结 Chapter 11 名字与地址转换

    本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务器名字和端口 ...

  6. 网络编程学习——名字与地址转换(一)

    2019独角兽企业重金招聘Python工程师标准>>> 1 域名系统 域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射.主机名既可以是一个 ...

  7. UNP编程:31---名字与地址转换之(服务解析函数:getservbyname()、getservbyport()、struct  servent)

    一.服务与地址的关系 一个端口代表一个服务(服务进程),因此可以使用服务名来代替端口的使用 像主机一样,服务也通常靠名字来认知 如果我们在程序代码中通过其名字而不是其端口号来指代一个服务,而且从名字到 ...

  8. 《UNIX网络编程:套接字联网API》啃书笔记(第8UDP套接字编程、11章地址转换)

    基本UDP套接字编程 下图为UDP客户/服务器程序的函数调用: 注意客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数.类似的,服务器不接受来自客 ...

  9. (域名、主机名、服务名、端口号)名字与地址的转换 (gethostbyname、getservbyname、getaddrinfo、getnameinfo等)

    转载地址 本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务器 ...

最新文章

  1. JAVA语言基础-面向对象(集合框架02List、泛型)
  2. python列表知识点_Python列表List基础知识点总结
  3. Javascript的变量作用域居然可以跨越多个函数!
  4. showModalDialog页面
  5. 大疆地理围栏系统预防无人机闯入机场
  6. c++调用cplex求解例子_c++调用CPLEX环境配置
  7. UFT11.5如何复用QTP9.2的脚本
  8. python 计算流形上两点之间的测地距离
  9. android 空格的转义字符,Android 常用转义字符
  10. matlab 神经网络设计与应用,MATLAB 神经网络设计与应用(最新版)
  11. ACE_TAO 017 ACE_wrappers\examples\Reactor
  12. 强不知以为知 怎能善其事
  13. k8s实战入门——Service
  14. Python处理图像文件的实用姿势
  15. openmvg中cmd模块解析
  16. 群晖NAS误删30T数据成功恢复全过程
  17. 一碗牛肉面的成本是多少钱?
  18. 解决 npm install 时出现的.git can‘t be found (see https://git.io/Jc3F9)的问题 run `npm fund` for details问题解决
  19. Netty源码剖析之内存池和对象池设计流程
  20. knockout学习笔记

热门文章

  1. 计算机调查取证分析篇
  2. 推荐一款 最方便的 阅读blog的软件---SharpReader!
  3. 实现盒子动画和键盘特效
  4. 数据结构链表——JavaScript的实现
  5. 【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra
  6. 阿里云ECS上环境搭建(virtualenv+flask+gunicorn+supervisor+nginx)
  7. MS SQL 维护小记
  8. RecyclerView显示加载多种布局的原理
  9. 算法------数组---------存在重复元素
  10. JNI调用c++函数,该函数的参数是结构体(——对象的传递)