网络编程学习(八)

gethostbyname和gethostbyaddr在主机名字与IPv4地址之间进行转换,

getservbyname和getservbyport在服务名字和端口号之间进行转换,

getaddrinfo和getnameinfo分别用于主机名字和IP地址之间以及服务名字和端口号之间的转换。


域名系统DNS主要用于主机名字与IP地址之间的映射。

主机名字既可以是一个简单名字,也可以是一个全限定域名。


解析器代码通过读取其系统相关配置文件确定本组织机构的名字服务器们的所在位置。文件/etc/resolv.conf通常包含本地名字服务器主机的IP地址。

解析器使用UDP向本地名字服务器发出查询。如果本地名字服务器不知道答案,它通常就会使用UDP在整个因特网上查询其他名字服务器。如果答案太长,超出了UDP消息的承载能力,本地名字服务器和解析器会自动切换到TCP。


gethostbyname与我们介绍过的其他套接字函数的不同之处在于:当发生错误时,它不设置errno变量,而是将全局整数变量h_errno设置为头文件<netdb.h>中定义的下列常指之一:HOST_NOT_FOUND,TRY_AGAIN,NO_RECOVERY,NO_DATA(NO_ADDRESS)。NO_DATA错误表示指定的名字有效,但是它没有A记录。只有MX记录的主机名就是这样的一个例子。

如今多数解析器提供名为hsterror的函数,它以某个h_errno值作为惟一的参数,返回的是一个const char *指针,只想相应错误的说明。


像主机一样,服务也通常靠名字来认知。如果我们在程序代码中通过其名字而不是其端口号来指定一个服务,而且从名字到端口号的映射关系保存在一个文件中(通常是/etc/services),那么即使端口号发生变动,我们需修改的仅仅是/etc/services文件中的某一行,而不必重新编译应用程序。


服务器名参数servname必须指定。如果同时制定了协议(即protoname参数为非空指针),那么指定服务必须有匹配的协议。有些因特网服务既用TCP也用UDP提供,其他因特网服务则仅仅支持单个协议(例如FTP要求使用TCP)。如果protoname未指定而servname指定服务支持多个协议,那么返回哪个端口号取决于实现。通常情况下这种选择无关紧要,因为支持多个协议的服务往往使用相同的TCP端口号和UDP端口号,不过这点没有保证。


servent结构中我们关心的主要字段是端口号。既然端口号是以网络字节序返回的,把它存放到套接字地址结构时绝对不能调用htons。


getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构而不是一个地址列表。这些sockaddr结构随后可由套接字函数直接使用。

如此一来,getaddrinfo函数把协议相关性完全隐藏在这个库函数内部。应用程序只需处理由getaddrinfo填写的套接字地址结构。


假设我们调用getaddrinfo,遍历返回的addrinfo结构链表后找到所需的结构。如果我们为保存其信息仅仅复制这个addrinfo结构,然后调用freeaddrinfo,那就引入了一个潜藏的错误。原因在于这个addrinfo结构本身指向动态分配的内存空间(用于存放套接字地址结构和可能有的规范主机名),因此由我们保存的结构指向的内存空间已在调用freeaddrinfo时返还给系统,稍后可能用于其他目的。


只复制这个addrinfo结构而不复制由它转而指向的其他结构称为浅复制(shallow copy)。既复制这个addrinfo结构又复制由它指向的所有其他结构称为深复制(deep copy)。


getnameinfo是getaddrinfo的互补函数,它以一个套接字地址为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。


函数重入问题:

某些函数共享同一个静态变量,传统的版本不可以重入;后续支持线程的一些实现则提供了可重入的版本

我们不应改从信号处理函数中调用标准I/O函数,因为许多版本的标准I/O函数库是不可重入的


有两种方法可以把诸如gethostbyname之类不可重入的函数改为可重入函数:

1.把由不可重入函数填写并返回静态结构的做法改为由调用者分配再由可重入函数填写结构(增加调用者的负担)

2.由可重入函数调用malloc以动态分配内存空间(必须调用对应的free函数来释放分配的内存)


你可能感兴趣的:(网络编程学习(八))