![第06章名字与地址转换编程_第1页](http://file4.renrendoc.com/view/5777999c6f5f278fa3964144a287f06b/5777999c6f5f278fa3964144a287f06b1.gif)
![第06章名字与地址转换编程_第2页](http://file4.renrendoc.com/view/5777999c6f5f278fa3964144a287f06b/5777999c6f5f278fa3964144a287f06b2.gif)
![第06章名字与地址转换编程_第3页](http://file4.renrendoc.com/view/5777999c6f5f278fa3964144a287f06b/5777999c6f5f278fa3964144a287f06b3.gif)
![第06章名字与地址转换编程_第4页](http://file4.renrendoc.com/view/5777999c6f5f278fa3964144a287f06b/5777999c6f5f278fa3964144a287f06b4.gif)
![第06章名字与地址转换编程_第5页](http://file4.renrendoc.com/view/5777999c6f5f278fa3964144a287f06b/5777999c6f5f278fa3964144a287f06b5.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章
名字与地址转换编程知识点:
域名系统作用域名系统对应记录从域名解析对应IP从IP解析对应域名现在的网络都是使用名字来访问服务器的,而不是使用地址来访问。那它们是怎么转换的呢?答案就是利用名字与地址的转换函数实现的:gethostbyname和gethostbyaddr在主机名字与IP地址间进行转换,getservbyname和getservbyport在服务名字和端口号间进行转换,并且还要介绍两个与协议无关的getaddrinfo和getnameinfo函数域名系统就是通常所说的DNS(DomainNameSystem)系统,而DNS系统主要用于主机名与IP地址间的映射,同时还可以通过主机名或主机地址获取主机的相关信息。那么在DNS系统中是怎样实现主机名与IP地址间的映射的呢?答案就是通过资源记录来实现。在DNS中的条目称为资源记录RR(ResourceRecord),它们主要有以下几类:(1)A。A记录将一个域名地址对应一个32bit的IPv4地址。(2)AAAA。AAAA记录(称为“四A”记录)将主机名映射为128位的IPv6地址。(3)NS。NS记录用于指定一个域名服务器,它负责定义由哪个域名服务器负责管理维护本区域的记录。(4)MX.MX记录用于指定一台主机的域名,所有发送到本域的电子邮件都由这台主机接收。(5)PTR。PTR记录(称为“指针记录”)将IP地址映射为主机名。(6)CNAME。CNAME代表CanonicalName(规范名字),作用是允许主机建立别名。名字解析过程应用程序代码解析器代码解析器配置文件函数调用函数返回本地名字服务器其他名字服务器UDP请求UDP应答应用进程gethostbyname()函数找主机名最基本的函数gethostbyname(),该函数执行如果成功,它返回一个指向结构hostent的指针,该结构中包含了该主机的所有IPv4地址或IPv6地址;如果失败返回空指针。#include<netdb.h> structhostent*gethostbyname(constchar*hostname);参数hostname是主机的域名地址,函数将查询的结果作为参数返回。如果失败返回空指针;如果成功此参数返回的非空指针指向如下的hostent结构:结构hostentstructhostent { char*h_name; /*主机的正式名称*/ char**h_aliases; /*主机的别名列表*/ int h_addrtype; /*主机地址类型*/ int h_length;
/*主机地址长度*/ char**h_addr_list; *主机IP地址的列表*/ };#defineh_addrh_addr_list[0] /*在列表中的第一个地址*/gethostbyname返回的信息h_nameh_aliasesh_addrtypeh_lengthh_addr_listhostent{}正式主机名\0别名1\0别名2\0IP地址1IP地址2IP地址3NULLNULLAF_INETin_addr{}in_addr{}in_addr{}gethostbyname()函数是怎样工作的原来gethostbyname函数首先在/etc/hosts文件中查找是否有匹配的主机名。如果没有,则根据在域名解析配置文件/etc/resolv.conf中指定的本地域名服务器的地址向本地域名服务器发送地址解析请求。如果本地域名服务器能够解析,则返回UDP数据包说明结果,否则本地域名服务器将向上一层的域名服务器发送域名解析请求。下面举个例子来看看gethostbyname()函数#include<netinet/in.h>#include<arpa/inet.h>#include<netdb.h>
main(intargc,constchar**argv){ulong_taddr;structhostent*hp;char**p;if(argc!=2){(void)printf("usage:%shost_name\n",argv[0]);exit(1);}hp=gethostbyname(argv[1]);if(hp==NULL){(void)printf("hostinformationfor%snotfound\n",argv[1]);exit(2);}for(p=hp->h_addr_list;*p!=0;p++){structin_addrin;char**q;(void)memcpy(&in.s_addr,*p,sizeof(in.s_addr));(void)printf("%s\t%s",inet_ntoa(in),hp->h_name);for(q=hp->h_aliases;*q!=0;q++)(void)printf("%s",*q);(void)putchar('\n');}exit(0);}RES_USE_INET6解析器选项解析器选项是用来通知解析器让gethostbyname返回的是IPv6地址而不是IPv4地址。设置该选项的方法有以下3种:(1)应用程序可以设置此选项(2)如果环境变量RES_OPTIONS中含有串inet6,则此选项打开(3)在配置文件中如果包含“optionsinet6”时选项打开,但如果这样设置了,则此选项影响主机上调用解析器函数的所有应用程序,因此这项技术要在结构hostent中返回的IPv6地址可以被主机上的所有应用程序处理时才能使用。gethostbyname2函数对IPv6的支持gethostbyname2()函数,该函数有两个参数,并允许指定地址族。#include<netdb.h>structhostent*gethostbyname2(constchar*hostname,intfamily)返回:若为非空指针,则表示成功;若为空指针,则表示出错,同时设置h_errno。该函数的返回值与gethostbyname的返回值相同,为一个指向结构hostent的指针。该函数的逻辑依赖于参数family和解析器选项RES_USE_INET6反向域名解析函数gethostbyaddr()gethostbyaddr()函数的作用是可以查询指定的IP地址对应的主机域名地址。函数的形式如下:#include<netdb.h>structhostent*gethostbyaddr(constchar*addr,size_tlen,intfamily);返回:若为非空指针,则表示成功;若为空指针,则表示出错,同时设置h_errno。该函数同样返回一个指向结构hostent的指针。而在参数中,参数addr不是char*类型,而是一个真正指向含有IPv4或IPv6地址的结构in_addr或in6_addr的指针;len是此结构的大小,对于IPv4地址为4,对于IPv6地址为16;参数family为AF_INET或AF_INET6。main(intargc,constchar**argv){ulong_taddr;structhostent*hp;char**p;if(argc!=2){printf("usage:%sIP-address\n",argv[0]);exit(1);}if(((addr=inet_addr(argv[1]))==-1){printf("IP-addressmustbeoftheforma.b.c.d\n");exit(1);}hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);if(hp==NULL){printf("hostinformationfor%snotfound\n",argv[1]);exit(1);}for(p=hp->h_addr_list;*p!=0;p++){structin_addrin;char**q;memcpy(&in.s_addr,*p,sizeof(in.s_addr));printf("%s\t%s",inet_ntoa(in),hp->h_name);for(q=hp->h_aliases;*q!=0;q++)printf("%s",*q);}exit(0);}[root@localhosttest]#./blocalhost.localdomainlocalhost地址转换例子#include<netdb.h>#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>intmain(intargc,char*argv[]){structsockaddr_inaddr;structhostent*he;char**alias;if(argc<2){printf("Usage:%sname|IP",argv[0]);exit(1);}地址转换例子(cont.)
argv++;for(;*argv!=NULL;argv++){if(inet_aton(*argv,&addr.sin_addr)!=0){he=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);printf("addressinformationofIP%s:\n",*argv);}else{he=gethostbyname(*argv);printf("addressinformationofhost%s:\n",*argv);}if(he==NULL){printf("noaddressinformationof%s\n",*argv);continue;}地址转换例子(cont.)
printf("Officaialhostname:%s\n",he->h_name);printf("namealiases:");for(alias=he->h_aliases;*alias!=NULL;alias++)printf("%s\n",*alias);printf("\nIPaddresses:");for(alias=he->h_addr_list;*alias!=NULL;alias++)printf("%s\n",inet_ntoa(*(structin_addr*)(*alias)));}}uname()函数该函数功能如同它的名字一样,即返回当前主机的名字,它返回的信息比较完整。下一节将介绍返回主机名字的另一个函数,但它返回的信息就相对少些。经常与函数gethostbyname()一起用来确定本地主机的IP地址。它的具体形式如下:#include<sys/utsname.h> intuname(structutsname*name);返回:若为非负值,则表示成功;若为-1,则表示出错。uname函数该函数返回本机的名字,它不是解析器库中的一部分,但它经常与gethostbyname函数一起用来确定本地主机IP地址;函数的参数是“值-结果”参数,其类型如下:#defineUTS_NAMESIZE 16#defineUTS_NODESIZE 256structutsname{ charsysname[UTS_NAMESIZE]; /*nameofthisoperatingsystem*/ charnodename[UTS_NODESIZE]; /*主机的名字*/ charrelease[UTS_NAMESIZE]; /*O.S内部版本*/ charversion[UTS_NAMESIZE]; /*O.S发行版本号*/ charmachine[UTS_NAMESIZE]; /*硬件版本*/}获取本机IP地址#include<netdb.h>#include<sys/utsname.h>#include<netinet/in.h>#include<stdio.h>intmain(void){structhostent*he;structutsnamemyname;structin_addraddr;chartemp[100];if(uname(&myname)<0)return(0);获取本机IP地址(续)
if((he=gethostbyname(myname.nodename))==NULL){perror("error.");exit(1);}printf("OSnameis%s\n",myname.sysname);printf("nodenameis%s\n",myname.nodename);printf("OSversionreleaseis%s\n",myname.release);printf(“hardwareversionis%s\n”,myname.version);printf("hardwaretypeis%s\n",myname.machine);memcpy(&addr,he->h_addr,sizeof(addr));printf("hostIPis%s\n",inet_ntoa(addr));return(0);}gethostname函数该函数同样返回当前主机的名字,但返回信息比上节介绍的函数少,该函数形式如下:#include<unistd.h>int gethostname(char*name,size_tnamelen);返回:若为0,则表示成功;若为-1,则表示出错。name是指向主机名存储位置的指针,namelen是此数组的大小。如果有空间,主机名以空字符结束。主机名的最大长度通常是由头文件<sys/param.h>定义的常值MAXHOSTNAMELEN。#include<unistd.h>intmain(){charbuf[100];size_tnamelen;namelen=sizeof(buf);gethostname(buf,namelen);printf("hostnameis%s\n",buf);}getservbyname和getservbyport函数函数getservbyname()可以通过服务名来获取服务器端口号,函数的形式如下:#include<netdb.h>structservent*getservbyname(constchar*servname,constchar*protoname);返回:若为非空指针,则表示成功;若为空指针,则表示出错。函数是getservbyport()函数,该函数的作用是通过端口号来获取服务名,它是反向的查询服务名称,即功能与getservbyname()函数相反。下面就来看一下它的具体形式:#include<netdb.h>structservent*getservbyport(intport,constchar*protname);返回:若为非空指针,则表示成功;若为空指针,则表示出错。获取服务名字函数该函数根据服务器提供的服务名字获取服务器的有关信息;在/etc/services文件中查找。参数servname指向服务的名字,如”domain”,”ftp”,”www”;函数返回的结构如下:structservent{ char *s_name; /*officialservicename*/ char **s_aliases; /*aliaslist*/ int s_port; /*portnumber,net-byteorder*/ char *s_proto; /*protocoltouse*/}如:structservent*ptr;sptr=getservbyname(“FTP”,“TCP”);#include<netdb.h>intmain(){structservent*ptr;ptr=getservbyname("ssh",NULL);if(ptr==NULL){perror("error");exit(0);}printf("portis:%d",ntohs(ptr->s_port));}获取服务名字函数(cont.)#include<netdb.h>structservent*getservbyport(intport,constchar*protoname);
返回:非空指针-成功;空指针-出错。该函数根据端口号和可选的协议查找相应的服务;参数port必须为网络字节序;如:structservent*sptr;sptr=getservbyport(htons(53),“UDP); /*DNSusingUDP*/sptr=getservbyport(htons(21),“TCP”); /*FTPusingTCP*/sptr=getservbyport(htons(21),NULL); /*FTPusingTCP*/sptr=getservbyport(htons(21),”UDP”); /*thiscallwillfail*/#include<netdb.h>#include<errno.h>#include<stdio.h>intmain(){structservent*srv;inti;for(i=1;i<100;i++){srv=getservbyport(htons(i),NULL);if(srv==NULL){printf("PORT%dnoservice\n",i);}elseprintf("PORT:%dservice:%s\n",i,srv->s_name);}}获取套接字的地址信息#include<sys/socket.h>intgetsockname(intsockfd,structsockaddr*localaddr,socklen_t*len);intgetpeername(intsockfd,structsockaddr*peeraddr,socklen_t*len);
均返回:0-成功;-1-出错getsockname函数返回套接字的本地地址;而后者返回套接字对应的远程地址;一般在以下三中情况下调用这两个函数:客户机在connect后,用getsockname获取系统选择的ip和端口号;以INADDR_ANY调用bind的服务器,在接收到连接后(即accept后),调用getsockname获取系统选择的IP地址;服务器获得连接后,并exec真正的程序后。只能用getpeername获取客户套接字地址;getaddrinfo、gai_strerror和host_serv函数getaddrinfo()函数在库函数中隐藏了所有协议依赖性,因此应用程序只需要处理由getaddrinfo填写的套接口地址结构即可。该函数的形式如下:#include<netdb.h>intgetaddrinfo(constchar*hostname,constchar*service,conststructaddrinfo*hints,structaddrinfo**result);返回:若成功返回0,若出错返回非0。getaddrinfo函数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(用户数据报协议)getaddrinfo函数ai_flags指定了如何来处理地址和名字,可取值如下:
gai_strerror()函数,该函数返回的非零错误值的名字和含义如表6-2所示。gai_strerror()函数以这些值作为它的一个参数,返回指向对应的出错信息字符串的指针。该函数形式如下:# include<netdb.h> char*gai_strerror(interror);返回:一个指向描述出错信息字符串的指针。填写hints结构的函数,即host_serv()函数,它以地址族和套接口类型作为参数,形式如下:#include"unp.h"structaddrinfo*host_serv(constchar*hostname,constchar*service,int
family,intsocktype);返回:若成功返回指向addrinfo结构的指针,若出错返回NULL。freeaddrinfo函数getaddrinfo()函数返回的存储空间,包括addrinfo结构、ai_addr结构和ai_canonname字符串,都是用malloc动态获取的。这些空间可调用freeadddrinfo()函数释放。下面就来看一下这个函数。#include<netdb.H>voidfreeaddrinfo(structaddrinfo*ai);该函数的参数ai指向getaddrinfo返回的第一个addrinfo结构,因此该链表中的所有结构和这些结构所指向的动态存储空间都将被释放。这样可以通过它来回收资源,同时如果在getaddrinfo()函数中调用某个操作时失败,也可以调用freeaddrinfo()函数来对错误进行处理。使用getaddrinfo的TCP和UDP有了host_serv()函数后,现在来看一下getaddrinfo在TCP中的应用,先从tcp_connect()函数着手,该函数执行客户程序的一般操作步骤是:创建一个TCP套接口并与服务器建立连接,它的形式如下:#include"unp.h"inttcp_connect(constchar*hostname,constchar*service);返回:若成功则返回已连接套接口的描述字,若出错则不返回。#include"unp.h"
inttcp_connect(constchar*host,constchar*serv){intsockfd,n;structaddrinfohints,*res,*ressave;bzero(&hints,sizeof(structaddrinfo));hints.ai_family=AF_UNSPEC;hints.ai_socktype=SOCK_STREAM;if((n=getaddrinfo(host,serv,&hints,&res))!=0)err_quit("tcp_connecterrorfor%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; /*ignorethisone*/if(connect(sockfd,res->ai_addr,res->ai_addrlen)==0)break; /*success*/Close(sockfd); /*ignorethisone*/}while((res=res->ai_next)!=NULL);if(res==NULL) /*errnosetfromfinalconnect()*/err_sys("tcp_connecterrorfor%s,%s",host,serv);freeaddrinfo(ressave);return(sockfd);}当用此函数时如果getaddrinfo()失败,或所有connect()失败,这个函数(以及在以下描述的给getaddrinfo()提供一些简单接口的其他函数)将终止。只有在成功时这个函数才返回。接下来看一下另一个函数tcp_listen(),它是用来执行TCP服务器程序的一般操作步骤,即创建一个TCP套接口,给它捆绑服务器的众所周知端口,并允许接收外来的连接请求,该函数形式如下:#include"unp.H"inttcp_listen(constchar*hostname,constchar*service,socklen_t*lenptr);返回:若成功返回已连接套接口描述字,若出错则不返回。#include"unp.h"
inttcp_listen(constchar*host,constchar*serv,socklen_t*addrlenp){intlistenfd,n;constinton=1;structaddrinfo hints,*res,*ressave;bzero(&hints,sizeof(structaddrinfo));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_listenerrorfor%s,%s:%s",host,serv,gai_strerror(n));ressave=res;do{listenfd=socket(res->ai_family,res->ai_socktype,res->ai_protocol);if(listenfd<0) continue; /*error,trynextone*/Setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));if(bind(listenfd,res->ai_addr,res->ai_addrlen)==0)break; /*success*/Close(listenfd); /*binderror,closeandtrynextone*/}while((res=res->ai_next)!=NULL);if(res==NULL) /*errnofromfinalsocket()orbind()*/err_sys("tcp_listenerrorfor%s,%s",host,serv);Listen(listenfd,LISTENQ);if(addrlenp)*addrlenp=res->ai_addrlen; /*returnsizeofprotocoladdress*/freeaddrinfo(ressave);return(listenfd);}到现在为止,已用以上两个函数的源码介绍了getaddrinfo()函数在TCP中的一些应用,这将对理解getaddrinfo()函数有很大的帮助,但是这是在TCP中的应用,那么在UDP中又是怎样的情况呢?同样分别通过udp_client()、udp_connect()、udp_server()三个函数来解答这个问题。因为提供了一个创建未连接UDP套接口的客户函数给getaddrinfo(),所以在UDP中getaddrinfo()函数的应用有所改变。而另一个函数udp_connect()则提供一个创建已连接UDP的套接口。首先来看一下udp_client()函数,该函数的作用是创建一个未连接UDP套接口,返回3项数据:第一,返回值是套接口描述字;第二,saptr是一个指向套接口地址结构(由udp_client动态分配)的指针(由调用者声明)的地址,在这个结构中存放目的IP地址和端口号,用来调用sendto(),套接口地址结构的大小在1enp指向的变量中返回;最后一个参数不能是空指针(tcp_listen的最后一个参数是允许的),因为套接口地址结构的长度在调用函数sendto()和recvfrom()时都是需要的,该函数的形式如下:#include"unp.h"intudp_client(constchar*hostname,constchar*service,void**saptr,
socklen_t*lenp);返回:若成功返回未连接套接口的描述字,若出错则不返回。#include"unp.h"
intudp_client(constchar*host,constchar*serv,void**saptr,socklen_t*lenp){intsockfd,n;structaddrinfo hints,*res,*ressave;bzero(&hints,sizeof(structaddrinfo));hints.ai_family=AF_UNSPEC;hints.ai_socktype=SOCK_DGRAM;if((n=getaddrinfo(host,serv,&hints,&res))!=0)err_quit("udp_clienterrorfor%s,%s:%s",host,serv,gai_strerror(n));ressave=res;do{sockfd=socket(res->ai_family,res->ai_socktype,res->ai_protocol);f(sockfd>=0)break; /*success*/}while((res=res->ai_next)!=NULL);if(res==NULL) /*errnosetfromfinalsocket()*/err_sys("udp_clienterrorfor%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_connect()函数,该函数创建一个已连接UDP套接口,该函数形式如下:#include"unp.h"intudp_connect(constchar*hostname,constchar*service);返回:如果成功则返回套接口描述字,若出错则不返回。当得到已连接UDP套接口后,在udp_client()函数中所需的后两个参数就不再需要了。用户可以用write函数代替sendto函数,于是就不再需要返回套接口地址结构和它的长度了。
intudp_connect(constchar*host,constchar*serv){intsockfd,n;structaddrinfohints,*res,*ressave;bzero(&hints,sizeof(structaddrinfo));hints.ai_family=AF_UNSPEC;hints.ai_socktype=SOCK_DGRAM;if((n=getaddrinfo(host,serv,&hints,&res))!=0)err_quit("udp_connecterrorfor%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; /*ignorethisone*/if(connect(sockfd,res->ai_addr,res->ai_addrlen)==0)break; /*success*/Close(sockfd); /*ignorethisone*/}while((res=res->ai_next)!=NULL);if(r
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《庖丁解牛练习题》课件
- 《万能险产品说明会》课件
- 《电机维护保养》课件
- 环境监测期末复习试题含答案
- 《针灸治疗面痛》课件
- 《贵金属柜面操作》课件
- 建筑电气设备安装识图与施工课件
- 《普通昆虫学绪论》课件
- 《压力容器材料》课件
- 物流运输货物损失快速处理与免责条款协议
- 2023年09月内蒙古赤峰学院招考聘用“双师型”教师2人笔试历年难易错点考题荟萃附带答案详解
- 高考语文复习:文言文简答题例析
- 物理化学全册电子教案
- 三年级英语上册整册书单词默写表学生版(外研版三起)
- 课本剧《刘姥姥进大观园》剧本
- 跌倒坠床的评估及预防课件
- 自闭症机构与家长协议书
- 《建筑防水构造(CPS反应粘结型防水材料)》
- 跨境电子商务基础与实务PPT全套完整教学课件
- 《研学旅行概论》课程标准
- 如愿三声部合唱简谱
评论
0/150
提交评论