本文其實是在說DNS的東西,以及如何運行時判斷使用IPv4還是IPv6。
chapter 3 Of Names and Address Families
1. 名字和數字(IP地址)的映射
人類記憶字符比記憶一串數字要更加在行,而且IP地址可能隨着ISP的更改而在變化,所以使用一種稱爲“名字系統”(Name System)以完成名字和數字之間的映射就成了勢在必行的東西。這便是DNS(Domain Name System)協議。
這裏主要使用到3個函數。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
/*
* getaddrinfo():one or more addrinfo structures,這個是由雙重指針results指向
* @params:
* hostStr: Internet host
* serviceStr: a service
* hints: specifies criteria, tells the system what kinds of endpoints the caller is interested in
* results:
* @return:
* 成功返回0
* 失敗返回非0值
*
*/
int getaddrinfo(const char *hostStr, const char *serviceStr,
const struct addrinfo *hints, struct addrinfo **results);
/*
* freeaddrinfo(): 釋放的是getaddrinfo返回的results參數所指向的。
* @params:
* addrList: 就是上一個的*results
* @return:
*/
void freeaddrinfo(struct addrinfo *addrList);
/*
* gai_strerror(): 把返回的int數字轉爲對應的錯誤信息
* @params:
* errorCode: 是getaddrinfo返回
* @return:
*/
const char *gai_strerror(int errorCode);
其中,getaddrinfo()
函數第四個參數是一個二重指針,是一個鏈表形式的。因爲對於一個域名來說,可能有多個IP地址或服務類型,如IPv4TCP、IPv6UDP,需要一一列舉出來。
這裏有一個使用上述函數的例子GetAddrInfo.c,這個函數還使用了Practical.h,DieWithMessage.c以及AddressUtility.c中的PrintSocketAddress()
方法。編譯的時候請注意。
2. 通用地址程序
Socket API允許我們把確定用IPv4還是IPv6地址的決定推遲到運行時來做。
最重要的其實是使用getaddrinfo()
傳參hints
時,把ai_family
設置爲AF_UNSPEC
。這樣就能夠既接收IPv4又接收IPv6地址了。
這裏使用到的程序是Practical.h,TCPEchoClient.c,TCPEchoServer.c,TCPClientUtility.c,TCPServerUtility.c,DieWithMessage.c,AddressUtility.c。
3. 從數字(IP地址)獲取名字
其實這個是getaddrinfo()
的反着用,從IP地址得到對應的名字,不過使用的是函數getnameinfo()
,而且它的行爲和getaddrinfo()
基本一致,返回值爲0則成功,非0也可以傳遞給gai_strerror()
以獲取對應的錯誤信息。
/*
* getnameinfo: the inverse of getaddrinfo(3): it converts a socket address
* to a corresponding host and service, in a protocol-independent manner.
* 【通過socket地址同時獲得以字符串表示的主機名和服務名】
* @params:
* addr: 地址
* addrlen: 地址長度
* host: 保存返回的主機名
* hostlen: 主機名長度
* serv: 保存返回的服務名
* servlen: 服務名長度
* flags:
* @return:
* 同getaddrinfo()
*/
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags);
有關這些函數的詳細內容,可以使用Man Pages查看,也可以參考Linux下C Socket編程基礎API。尤其注意struct addrinfo
中ai_flags
的設置以及getnameinfo()
中的flags的設置,二者都可以使用位或操作符|
。