网络编程学习——名字与地址转换(一)
2019獨角獸企業重金招聘Python工程師標準>>>
1 域名系統
? 域名系統(Domain Name System,DNS)主要用于主機名字與IP地址之間的映射。主機名既可以是一個簡單名字(simple name),例如mimiasd,也可以是一個全限定域名(Fully Qualified Domain Name,FQDN),例如mimiasd.unpbook.com。
2 gethostbyname函數
? 查找主機名最基本的函數是gethostbyname。如果成功,它就返回一個指向hostent結構的指針,該結構中含有所查找主機的所有IPv4地址。這個函數只能處理IPv4的地址。gethostbyname2函數通過設置AF值,可查看IPv6的地址。
#inlcude?<netdb.h> /*?Return?entry?from?host?data?base?for?host?with?NAME.This?function?is?a?possible?cancellation?point?and?therefore?notmarked?with?__THROW.??*/ extern?struct?hostent?*gethostbyname?(const?char?*__name);#ifdef?__USE_MISC /*?Return?entry?from?host?data?base?for?host?with?NAME.??AF?must?beset?to?the?address?type?which?is?`AF_INET'?for?IPv4?or?`AF_INET6'for?IPv6.This?function?is?not?part?of?POSIX?and?therefore?no?officialcancellation?point.??But?due?to?similarity?with?an?POSIX?interfaceor?due?to?the?implementation?it?is?a?cancellation?point?andtherefore?not?marked?with?__THROW.??*/ extern?struct?hostent?*gethostbyname2?(const?char?*__name,?int?__af);? 本函數返回的非空指針指向如下的hostent結構。
/*?Description?of?data?base?entry?for?a?single?host.??*/ struct?hostent {char?*h_name;????????????/*?Official?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?from?name?server.??*/ #if?defined?__USE_MISC?||?defined?__USE_GNU #?define????h_addr????h_addr_list[0]?/*?Address,?for?backward?compatibility.*/ #endif };?
圖1-1 hostent結構和它所包含的信息
? gethostbyname與我們介紹的其他套接字函數的不同之處在于:當錯誤發生時,它不設置errno變量,而是將全局整數變量h_errno設置為在頭文件<netdb.h>中定義的下列常值之一:
/*?Possible?values?left?in?`h_errno'.??*/ #?define?HOST_NOT_FOUND????1????/*?Authoritative?Answer?Host?not?found.??*/ #?define?TRY_AGAIN????2????/*?Non-Authoritative?Host?not?found,or?SERVERFAIL.??*/ #?define?NO_RECOVERY????3????/*?Non?recoverable?errors,?FORMERR,?REFUSED,NOTIMP.??*/ #?define?NO_DATA????4????/*?Valid?name,?no?data?record?of?requestedtype.??*/? 例子:
#include?<netdb.h> #include?<stdio.h> #include?<stdlib.h> #include?<arpa/inet.h>int?main(?int?argc,?char?**argv?) {char?*ptr,?**pptr;char?str[?INET_ADDRSTRLEN?];struct?hostent?*hptr;//?給每個命令行參數調用gethostnamewhile(?--argc?>?0?){//?輸出規范主機名ptr?=?*++argv;if(?(?hptr?=?gethostbyname(?ptr?)?)?==?NULL?){printf(?"?gethostbyname?error?for?host:?%s:?%s?",?ptr,?hstrerror(?h_errno?)?);continue;}printf(?"?official?hostname:?%s\n?",?hptr->h_name?);//?輸出別名列表for(?pptr?=?hptr->h_aliases;?*pptr?!=?NULL;?pptr++?)printf(?"?\talias:?%s\n?",?*pptr?);//?pptr指向一個指針數組,其中每個指針指向一個地址。對于每一個地址,我們調用inet_ntop并輸出返回的字符串switch(?hptr->h_addrtype?){case?AF_INET:pptr?=?hptr->h_addr_list;for(?;?*pptr?!=?NULL;?pptr++?)printf(?"?\taddress:?%s\n?",?inet_ntop(?hptr->h_addrtype,?*pptr,?str,?sizeof(?str?)?)?);break;default:printf(?"?unknown?address?type?"?);break;}}exit(?0?); }?
3 gethostbyaddr函數
? gethostbyaddr函數試圖由一個二進制的IP地址找到相應的主機名,與gethostbyname的行為正好相反。
#inlcude?<netdb.h> /*?Return?entry?from?host?data?base?which?address?match?ADDR?withlength?LEN?and?type?TYPE.This?function?is?a?possible?cancellation?point?and?therefore?notmarked?with?__THROW.??*/ extern?struct?hostent?*gethostbyaddr?(const?void?*__addr,?__socklen_t?__len,int?__type);?
4 getservbyname函數和getservbyport函數
? 像主機一樣,服務也通常靠名字來認識。如果沃恩在程序代碼中通過其名字而不是其端口號來指代一個服務,而且從名字到端口號的映射關系保存在一個文件中(通常是/etc/services),那么即使端口號發生變動,我們需要修改的僅僅是/etc/services文件中的某一行,而不是重新編譯應用程序。getservbyname函數用于根據給定查找應用服務。
#inlcude?<netdb.h> /*?Return?entry?from?network?data?base?for?network?with?NAME?andprotocol?PROTO.This?function?is?a?possible?cancellation?point?and?therefore?notmarked?with?__THROW.??*/ extern?struct?servent?*getservbyname?(const?char?*__name,?const?char?*__proto);? 本函數返回的非空指針指向如下的servent結構。
/*?Description?of?data?base?entry?for?a?single?service.??*/ struct?servent {char?*s_name;????????????/*?Official?service?name.??*/char?**s_aliases;????????/*?Alias?list.??*/int?s_port;????????????/*?Port?number.??*/char?*s_proto;????????/*?Protocol?to?use.??*/ };? 本函數的典型調用如下:
struct?servent?*sptr;sptr?=?getservbyname(?"?domain?",?"?udp?"?);?//?DNS?using?UDP sptr?=?getservbyname(?"?ftp?",?"?tcp?"?);?//?FTP?using?TCP sptr?=?getservbyname(?"?ftp?",?NULL?);?//?FTP?using?TCP sptr?=?getservbyname(?"?ftp?",?"?udp?"?);?//?this?call?will?fail? 下一個函數getservbyport用于根據給定端口號和可選協議查找相應服務。
#inlcude?<netdb.h> /*?Return?entry?from?service?data?base?which?matches?port?PORT?andprotocol?PROTO.This?function?is?a?possible?cancellation?point?and?therefore?notmarked?with?__THROW.??*/ extern?struct?servent?*getservbyport?(int?__port,?const?char?*__proto);? port函數的值必須為網絡字節序。本函數的典型調用如下;
struct?servent?*sptr;sptr?=?getservbyport(?hton(?53?),?"?udp?"?);?//?DNS?using?UDP sptr?=?getservbyport(?hton(?21?),?"?tcp?"?);?//?FTP?using?TCP sptr?=?getservbyport(?hton(?21?),?NULL?);?//?FTP?using?TCP sptr?=?getservbyport(?hton(?21?),?"?udp?"?);?//?this?call?will?fail? 必須清楚的是,有些端口號在TCP上用于一種服務,在UDP上卻用于完全不同的另一種服務。
? 例子:使用gethostbyname和getservbyname
#include?<netdb.h> #include?<stdio.h> #include?<stdlib.h> #include?<error.h> #include?<string.h> #include?<arpa/inet.h> #include?<sys/un.h> #include?<sys/types.h> #include?<sys/socket.h> #include?<netinet/in.h>#define?MAX_MESG_SIZE?1024char?*??sock_ntop(const?struct?sockaddr?*sa,?socklen_t?salen); char?*??Sock_ntop(const?struct?sockaddr?*sa,?socklen_t?salen);int?main(?int?argc,?char?**argv?) {int?sockfd,?n;char?recvline[?MAX_MESG_SIZE?+?1?];struct?sockaddr_in?servaddr;struct?in_addr?**pptr;struct?in_addr?*inetaddrp[?2?];struct?in_addr?inetaddr;struct?hostent?*hp;struct?servent?*sp;//?第一個命令行參數是主機名,我們把它作為參數傳遞給gethostbyname,第二個命令行參數是服務名,我們把它作為//?參數傳遞給getservbyname。假設我們的代碼使用TCP,我們把它作為getservbyname的第二個參數。如果//?gethostbyname名字查找失敗,我們就嘗試使用inet_aton函數,確定其參數是否已是ASCII格式的地址,若是則構造//?一個由相應的地址構成的單元素列表。if(?argc?!=?3?){printf(?"?usage:daytimecpcli1?<hostname>?<service>?\n"?);exit(?1?);}if(?(?hp?=?gethostbyname(?argv[?1?]?)?)?==?NULL?){?if(?inet_aton(?argv[?1?],?&inetaddr?)?==?0?){printf(?"?hostname?error?for?%s:?%s?\n",?argv[?1?],?hstrerror(?h_errno?)?);exit(?1?);}?else{inetaddrp[?0?]?=?&inetaddr;inetaddrp[?1?]?=?NULL;pptr?=?inetaddrp;}}else{pptr?=?(?struct?in_addr?**?)?hp->h_addr_list;}if(?(?sp?=?getservbyname(?argv[?2?],?"tcp"?)?)?==?NULL?){printf(?"?getservbyname?error?for?%s?\n",?argv[?2?]?);exit(?1?);}//?我們把對socket和connect的調用放在一個循環中,該循環為服務器主機的每個地址執行一次,直到//?connect成功或IP地址列表試完為止。調用socket以后,我們以服務器主機的IP地址和端口填裝網際網套接字//?地址結構。盡管我們可以把對bzero的調用和它后面的兩個賦值語句置于循環體之外以提高執行效率,不過下面代碼要//?簡介些。與服務器建立連接幾乎不會稱為網絡客戶的性能瓶頸。for(?;?*pptr?!=?NULL;?pptr++?){sockfd?=?socket(?AF_INET,?SOCK_STREAM,?0?);bzero(?&servaddr,?sizeof(?servaddr?)?);servaddr.sin_family?=?AF_INET;servaddr.sin_port?=?sp->s_port;memcpy(?&servaddr.sin_addr,?*pptr,?sizeof(?struct?in_addr?)?);printf(?"?trying?%s\n?",?Sock_ntop(?(?struct?sockaddr*?)?&servaddr,?sizeof(?servaddr?)?)?);//接著調用connect。如果調用成功,那就使用break語句終止循環,否則輸出一個出錯消息并關閉套接字。我們直到//?connect調用失敗的描述符必須關閉,不能再用。if(?connect(?sockfd,?(?struct?sockaddr*?)?&servaddr,?sizeof(?servaddr?)?)?==?0?)break;?//?successelse{printf(?"?connect?error?\n"?);close(?sockfd?);}}if(?*pptr?==?NULL?){printf(?"?unable?to?connect?\n"?);exit(?1?);}while(?(?n?=?read(?sockfd,?recvline,?MAX_MESG_SIZE?)?)?>?0?){recvline[?n?]?=?0;?//?null?terminatefputs(?recvline,stdout?);}exit(?0?); }?
5 getaddrinfo函數
? gethostbyname和gethostbyaddr這兩個函數僅僅支持IPv4。getadddrinfo函數能夠處理名字到地址以及服務到端口這兩種轉換,返回的是一個sockaddr結構而不是一個地址列表。
#include?<netdb.h> /*?Translate?name?of?a?service?location?and/or?a?service?name?to?set?ofsocket?addresses.This?function?is?a?possible?cancellation?point?and?therefore?notmarked?with?__THROW.??*/ extern?int?getaddrinfo?(const?char?*__restrict?__name,const?char?*__restrict?__service,const?struct?addrinfo?*__restrict?__req,struct?addrinfo?**__restrict?__pai);? 本函數通過__pai指針參數返回一個指向addrinfo結構鏈表的指針,而adddrinfo結構定義在頭文件<netdb.h>中。
/*?Structure?to?contain?information?about?address?of?a?service?provider.??*/ struct?addrinfo {int?ai_flags;????????????/*?Input?flags.??*/int?ai_family;????????/*?Protocol?family?for?socket.??*/int?ai_socktype;????????/*?Socket?type.??*/int?ai_protocol;????????/*?Protocol?for?socket.??*/socklen_t?ai_addrlen;????????/*?Length?of?socket?address.??*/struct?sockaddr?*ai_addr;????/*?Socket?address?for?socket.??*/char?*ai_canonname;????????/*?Canonical?name?for?service?location.??*/struct?addrinfo?*ai_next;????/*?Pointer?to?next?in?list.??*/ };? 在addrinfo結構中返回的信息可現在用于socket調用,隨后現成用于適合客戶的connect或senfto調用,或者是適合服務器的bind調用。
圖1-2 getaddrinfo返回信息的實例
? 端口53用于domain服務。這個端口在套接字地址結構中按照網絡字節序存放。返回的ai_protocol值或為IPPROTO_TCP,或為IPPROTO_UDP。
圖1-3 為每個IP地址返回的addrinfo結構的數目
?
6 gai_strerror函數
? 圖1-4給出了可由getaddrinfo返回的非0錯誤值的名字和含義。gai_strerror以這些值為它的唯一參數,返回一個指向對應的出錯信息串指針。
/*?Convert?error?return?from?getaddrinfo()?to?a?string.??*/ extern?const?char?*gai_strerror?(int?__ecode)?__THROW;
圖1-4 getaddrinfo返回的非0錯誤常值
7 freeaddrinfo函數
? 由getaddrinfo返回的所有存儲空間都使動態獲取的(譬如來自malloc調用),包括addrinfo結構、ai_addr結構和ai_cannonname字符串。這些誒儲存空間通過調用freeaddrinfo返還給系統。
#include?<netdb.h> /*?Free?`addrinfo'?structure?AI?including?associated?storage.??*/ extern?void?freeaddrinfo?(struct?addrinfo?*__ai)?__THROW;? __ai參數指向由getaddrinfo返回的第一個adddinfo結構。這個鏈表中的所有結構以及由它們指向的任何動態存儲空間都被釋放掉。
?
轉載于:https://my.oschina.net/u/2537915/blog/661144
總結
以上是生活随笔為你收集整理的网络编程学习——名字与地址转换(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决按照别人教程将系统中文名修改为英文名
- 下一篇: 学习C语言基础(1)C程序模板