UNP Chapter 11 - 高级名字与地址转换
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 - 高级名字与地址转换相关推荐
- UNP Chapter 9 - 基本名字与地址转换
9.1. 概述 本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在 ...
- Unix网络编程学习笔记之第11章 名字与地址转换
一. 域名系统(DNS) 1. 简介 DNS主要用于主机名和IP地址之间的映射. 主机名可以是简单的名字ljm,也可以是全限定域名ljm.localdomainbaidu.com等. 2.资源记录 D ...
- 名字与地址转换getservbyname 与 getservbyport函数
名字与地址转换getservbyname 与 getservbyport函数 服务也通常靠名字来标志,getservbyname函数用于根据给定名字查找相应服务. #include struct ...
- UNP编程:29---名字与地址转换之(域名系统)
一.域名系统的概念 域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射. 主机名既可以是一个简单名字(simple name),例如solaris或bsdi, ...
- UNP总结 Chapter 11 名字与地址转换
本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务器名字和端口 ...
- 网络编程学习——名字与地址转换(一)
2019独角兽企业重金招聘Python工程师标准>>> 1 域名系统 域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射.主机名既可以是一个 ...
- UNP编程:31---名字与地址转换之(服务解析函数:getservbyname()、getservbyport()、struct servent)
一.服务与地址的关系 一个端口代表一个服务(服务进程),因此可以使用服务名来代替端口的使用 像主机一样,服务也通常靠名字来认知 如果我们在程序代码中通过其名字而不是其端口号来指代一个服务,而且从名字到 ...
- 《UNIX网络编程:套接字联网API》啃书笔记(第8UDP套接字编程、11章地址转换)
基本UDP套接字编程 下图为UDP客户/服务器程序的函数调用: 注意客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数.类似的,服务器不接受来自客 ...
- (域名、主机名、服务名、端口号)名字与地址的转换 (gethostbyname、getservbyname、getaddrinfo、getnameinfo等)
转载地址 本章讲述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务器 ...
最新文章
- JAVA语言基础-面向对象(集合框架02List、泛型)
- python列表知识点_Python列表List基础知识点总结
- Javascript的变量作用域居然可以跨越多个函数!
- showModalDialog页面
- 大疆地理围栏系统预防无人机闯入机场
- c++调用cplex求解例子_c++调用CPLEX环境配置
- UFT11.5如何复用QTP9.2的脚本
- python 计算流形上两点之间的测地距离
- android 空格的转义字符,Android 常用转义字符
- matlab 神经网络设计与应用,MATLAB 神经网络设计与应用(最新版)
- ACE_TAO 017 ACE_wrappers\examples\Reactor
- 强不知以为知 怎能善其事
- k8s实战入门——Service
- Python处理图像文件的实用姿势
- openmvg中cmd模块解析
- 群晖NAS误删30T数据成功恢复全过程
- 一碗牛肉面的成本是多少钱?
- 解决 npm install 时出现的.git can‘t be found (see https://git.io/Jc3F9)的问题 run `npm fund` for details问题解决
- Netty源码剖析之内存池和对象池设计流程
- knockout学习笔记
热门文章
- 计算机调查取证分析篇
- 推荐一款 最方便的 阅读blog的软件---SharpReader!
- 实现盒子动画和键盘特效
- 数据结构链表——JavaScript的实现
- 【BZOJ3073】[Pa2011]Journeys 线段树+堆优化Dijkstra
- 阿里云ECS上环境搭建(virtualenv+flask+gunicorn+supervisor+nginx)
- MS SQL 维护小记
- RecyclerView显示加载多种布局的原理
- 算法------数组---------存在重复元素
- JNI调用c++函数,该函数的参数是结构体(——对象的传递)