本文其实是在说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的设置,二者都可以使用位或操作符|
。