在名字和數值地址間進行轉換的函數:
gethostbyname和gethostbyaddr:在主機名字與ipv4地址之間進行轉換。僅僅支持IPv4.
getservbyname和getservbyport:在服務名字和端口號之間進行轉換。
getaddrinfo和getnameinfo:用于主機名字和IP地址之間以及服務名字和端口號之間的轉換。(這兩個函數是協議無關的)
域名系統(Domain Name System,簡稱DNS)主要用于主機名字和IP地址之間的映射。
資源記錄
DNS中的條目稱為資源記錄(resource record,簡稱RR)。
RR類型:
A A記錄把一個主機名映射成一個32位的IPv4地址。
AAAA AAAA記錄把一個主機名映射成一個128位的IPv6地址。
PTR 稱為“指針記錄(pointer record)”,它把IP地址映射成主機名。
解析器和名字服務器
每個組織機構往往運行一個或多個名字服務器(name server),它們通常就是所謂的BIND(Berkeley Internet Name Domain)程序。應用程序通過調用稱為解析器(resolver)的函數庫中的函數接觸DNS服務器。常見的解析器函數是gethostbyname和gethostbyaddr:前者把主機名映射成IPv4地址,后者則執行相反的映射。
下圖展示了應用進程、解析器和名字服務器之間的一個典型關系。
解析器代碼通過讀取其系統相關配置文件確定本組織機構的名字服務器們(為可靠和冗余的目的,大多數組織機構運行多個名字服務器)的所在位置。文件/etc/resolv.conf通常包含本地名字服務器主機的IP地址。
DNS替代方法
不使用DNS也可能獲取名字和地址信息。常用的替代方法有靜態主機文件(通常是/etc/hosts文件)、網絡信息系統(Network Information System,簡稱NIS)以及輕權目錄訪問協議(Lightweight Directory access PRotocol,簡稱LDAP)。
查找主機名最基本的函數是gethostbyname。如果調用成功,它就返回一個指向hostent結構的指針,該結構中含有所查找主機的所有IPv4地址。這個函數的局限是只能返回IPv4地址,而getaddrinfo函數能夠同時處理IPv4地址和IPv6地址。
#include <netdb.h>struct hostent *gethostbyname(const char *hostname);返回值:非空指針——成功;空指針——出錯,同時設置h_errno
本函數返回的非空指針指向如下的hostent結構:
struct hostent { char *h_name; /* official(canonical) name of host */ char **h_aliases; /* pointer to array of pointers to alias names */ int h_addrtype; /* host address type: AF_INET */ int h_length; /* length of address: 4 */ char **h_addr_list; /* ptr to array of ptrs with IPv4 addrs */};
gethostbyname與其他套接口函數的不同之處在于:當發生錯誤時,它不設置errno變量,而是將全局整數變量h_errno設置在頭文件<netdb.h>中定義的下列常值之一:
HOST_NOT_FOUND
TRY_AGAIN
NO_RECOVERY
NO_DATA(等同于NO_ADDRESS)
多數解析器提供名為hstrerror的函數,它以某個h_errno值作為唯一的參數,返回的是一個const char *指針,指向相應錯誤的說明。
gethostbyaddr函數試圖由一個二進制IP地址找到相應的主機名,與gethostbyname的行為剛好相反。
#include <netdb.h>struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);返回值:非空指針——成功;空指針——出錯,同時設置h_errno
本函數返回一個同樣指向hostent結構的指針。
addr參數實際上不是char *類型,而是一個指向存放IPv4地址的某個in_addr結構的指針;
len參數是這個結構的大?。簩τ贗Pv4地址為4;
family參數為AF_INET。
服務也通??棵謥碚J知。如果我們在程序代碼中通過其名字而不是端口號來指代一個服務,而且從名字到端口號的映射關系保存在一個文件中(通常是/etc/services),那么即使端口號發生變動,我們需修改的僅僅是/etc/services文件中的某一行,而不必重新編譯應用程序。
getservbyname函數用于根據給定的名字查找相應服務。
#include <netdb.h>struct servent *getservbyname(const char *servname, const char *protoname);返回:非空指針——成功;空指針——出錯
本函數返回的非空指針指向如下的servent結構:
struct servent { char *s_name; /* official service name */ char **s_aliases; /* alias list */ int s_port; /* port number, network-byte order */ char *s_proto; /* protocol to use */};
服務名參數servname必須指定。如果同時指定了協議(即protoname參數為非空指針),那么指定服務必須有匹配的協議。有些因特網服務既用TCP也用UDP提供(例如DNS),其他因特網服務則僅僅支持單個協議(例如FTP要求使用TCP)。如果protoname未指定而servname指定服務支持多個協議,那么返回哪個端口號取決于實現。
servent結構中我們關心的主要是端口號。既然端口號是以網絡字節序返回的,把它存放到套接口地址結構時絕不能調用htons。
本函數的典型調用如下:
struct servent *sptr;sptr = getservbyname("domain", "udp");sptr = getservbyname("ftp", "tcp");
getservbyport函數用于根據給定端口號和可選協議查找相應服務。
#include <netdb.h>struct servent *getservbyport(int port, const char *protoname);返回:非空指針——成功;空指針——出錯
port參數的值必須為網絡字節序。本函數的典型調用如下:
struct servent *sptr;sptr = getservbyport(htons(53), "udp");sptr = getservbyport(htons(21), "tcp");
getaddrinfo函數能夠處理名字到地址以及服務到端口這兩種轉換,它解決了把主機名和服務名轉換成套接口地址結構的問題,返回的是一個sockaddr結構的鏈表而不是一個地址清單。這些sockaddr結構隨后可由套接口函數直接使用。
#include <netdb.h>int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);返回:0——成功;非0——出錯
本函數通過result指針參數返回一個指向addrinfo結構鏈表的指針,而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 */ socklen_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參數是一個主機名或地址串(IPv4的點分十進制數串或IPv6的十六進制數串)。
service參數是一個服務名或十進制端口號數串。
hints參數可以是一個空指針,也可以是一個指向某個addrinfo結構的指針,調用者在這個結構中填入關于期望返回的信息類型的暗示。
hints結構中調用者可以設置的成員有:
其中ai_flags成員可用的標志值及其含義如下:
AI_PASSIVE 套接口將用于被動打開。
AI_CANONNAME 告知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 按照所在主機的配置選擇返回地址類型,也就是只查找與所在主機回饋接口以外的網絡接口配置的IP地址版本一致的地址。
如果hints參數是一個空指針,本函數就假設ai_flags、ai_sokctype和ai_protocol的值均為0,ai_family的值為AF_UNSPEC。
如果本函數返回成功(0),那么由result參數指向的變量已被填入一個指針,它指向的是由其中的ai_next成員串接起來的addrinfo結構鏈表。可導致返回多個addrinfo結構的情形有以下兩個:
如果與hostname參數關聯的地址有多個,那么適用于所請求地址族(可通過hints結構的ai_family成員設置)的每個地址都返回一個對應的結構。
如果service參數指定的服務支持多個套接口類型,那么每個套接口類型都可能返回一個對應的結構,具體取決于hints結構的ai_socktype成員。
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 flags);返回:0——成功,非0——出錯
sockaddr指向一個套接口地址結構。
待返回的2個直觀可讀的字符串由調用者預先分配存儲空間:host和hostlen指定主機字符串;serv和servlen指定服務字符串。如果調用者不想返回主機字符串,那就指定hostlen為0。同樣,把servlen指定為0就是不想返回服務字符串。頭文件<netdb.h>中定義了2個常值用于分配這兩個存儲空間:NI_MAXHOST給出主機字符串存儲空間的最大長度,值為1025;NI_MAXSERV給出服務字符串存儲空間的最大長度,值為32.
6個可指定的標志flags,用于改變getnameinfo的操作:
常值 | 說明 |
NI_DGRAM NI_NAMEREQD NI_NOFQDN NI_NUMERICHOST NI_NUMERICSCOPE NI_NUMERICSERV | 數據報服務 若不能從地址解析出名字則返回錯誤 只返回FQDN的主機名部分 以數串格式返回主機字符串 以數串格式返回范圍標識字符串 以數串格式返回服務字符串 |
四類網絡相關信息總結如下表:
信息 | 數據文件 | 結構 | 鍵值查找函數 |
主機 網絡 協議 服務 | /etc/hosts /etc/networks /etc/protocols /etc/services | hostent netent protoent servent | gethostbyaddr,gethostbyname getnetbyaddr,getnetbyname getprotobyname,getprotobynumber getservbyname,getservbyport |
新聞熱點
疑難解答