第3章 关于名称和地址族
3.1 将名称映射到数字
socket API的大多数实现允许访问把名称映射到其他信息(包括Interenet地址)的名称
服务(name service)。可以把名称(www.baidu.com)映射到Internet地址,也可以把服务
(例如:应答echo)的名称映射到端口号。
解析:把名称映射到数值(地址或端口号)的过程。有很多种方式,一些涉及在后天与
其它系统交互,另一些严格在本地进行。
名称服务并不是使TCP/IP工作所必需的。名称只是提供一个间接层次,主机名服务可以访问
广泛来源的信息,主要是DNS(Domain Name System)和本地配置数据库。
DNS是一种分布式数据库,用于把像www.baidu.com这样的Domain name 映射到Internet地址
及其他信息。DNS协议允许连接到Internet的主机使用TCP或UDP从该数据库中检索信息。
本地配置数据库一般是用于名称-Internet地址映射的特定于操作系统的机制。
3.1.1 访问名称服务
将主机名称和服务名称映射到一个地址。
#include <sys/socket.h>
#include <netdb.h> //addrinfo
int getaddrinfo(const char *restrict host,
const char *restrict service,
const struct addrinfo *restrict hint,
struct addrinfo **restrict res);
返回值:若成功返回0,出错返回非0错误码
需要提供主机名称或地址,服务名称或端口号。若只提供一个名字,另一个必须为空指针。
主机名称可以是一个节点名或点分十进制法表示的主机地址。
第三个参数是要返回的信息的种类,可以使用一个可选的hint来选择地址,hint是一个过滤
地址的模版。仅使用ai_family, ai_flag, ai_protocol和ai_socktype字段。其它整数字段必须
设为0,并且指针字段为空。它表明系统调用者对那一类端点感兴趣。ai_socktype和ai_protocol
设为0:表示我们希望接受所有可能的数据。若hint为NULL:系统被期望处理这种情况,等价于ai_famliy
设为AF_UNSPEC(允许返回任何地址包括AF_INET和AF_INET6),其它字段为0。
ai_flags中所用的标志用来指定如何处理地址和名字:
ai_flags 描述
AI_ADDRCONFIG 查询配置的地址类型(IPv4或IPv6)。设置此标志仅当系统具有特定的地址配置
的接口时,getaddrinfo()才会返回该地址族的地址。因此仅当系统具有IPv4的接口
时,才会返回IPv4地址,对于IPv6同样。
AI_ALL 查询IPv4和IPv6地址(仅用于AI_V4MAPPED)
AI_CANONNAME 需要一个规范名(而不是主机名),类似于一个名称可以解析为许多数字地址一样。
可以把多个名称解析成一个IP地址。不过,其中有一个名称被解析为正式(规范)
名称。
AI_NUMERICHOST 以数字格式返回主机地址。如果host不是一个具有有效的数字地址格式的字符串,
这个标志导致返回一个错误。
AI_NUMERICSERV 以端口号返回服务
AI_PASSIVE 套接字地址用于监听绑定。若后host为NULL,任何返回的addrinfo都将把它们的地址
设置为合适的“任意”常量。INADDR_ANY(IPv4)或IN6ADDR_ANY_INIT(IPv6).
AI_V4MAPPED 如果没有找到IPv6地址,则返回映射到IPv6格式的IPv4地址。如果ai_family为
AF_INET6,并且没有找到匹配的IPv6地址,那么getaddrinfo()返回IPv4映射的IPv6
地址。仅支持IPv4和IPv6主机之间有限的互操作性。
第四个参数是存储一个指向包含结果的链表的指针。
函数getaddrinfo返回一个结构addrinfo的链表,使用freeaddrinfo释放一个或多个结构,
这取决于用ai_next字段链接起来的结构多少。
addrinfo至少包含:
struct addrinfo {
int ai_flags; /* customize behavior */
int ai_family; /* address family */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol */
socklen_t ai_addrlen; /* length in bytes of address*/
sturct sockaddr *ai_addr; /* address */
char *ai_canonname /* canonical name of host */
struct addrinfo *ai_next; /* next in list */
...
}
为什么使用一个链表,第一:对于主机和服务的每种组合,可能有地址族(v4或v6)和
套接字类型/协议(流tcp或数据包udp)的多种不同组合表示可能端口。例如:“server.example.net”
可能具有在v4/tcp或v6/udP上的端口1001上进行“垃圾邮件”服务侦听的多个实例。getaddrinfo
可以返回这两者。第二,主机名称可以映射到多个ip地址。
两个辅助函数:
#include <sys/socket.h>
#include <netdb.h>
void freeaddrinfo(struct addrinfo *addrlist);
#include <netdb.h>
const char *gai_strerror(int errorcode);
如果,getaddrinfo失败,不使用perror或strerror生成错误信息,而是用gai_strerror将返回的错误码转换为
错误信息。
函数getnameinfo将地址转换成主机名或服务名(Internet地址获取主机名称)
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *restrict addr,
socklen_t alen, char *restrict host,
socklen_t hostlen, char *restrict service,
socklen_t servlen, unsinged int flags);
套接字地址(addr)被转换成主机名或服务名。若host非空,它指向一个长度为hostlen字节的缓冲
区用于存储返回的主机名。若service非空,它指向一个长度为servlen字节的缓冲区用于存储返回的
服务名。
flags 描述
NI_DGRAM 服务基于数据报非基于流
NI_NAMEREQD 如果找不到主机名称,将其作为一个错误对待
NI_NOFQDN 对于本地主机,仅返回完全限定域名的字节名字部分
NI_NUMERICHOST 以数字形式而非名字返回主机地址
NI_NUMERICSERV 以数字形式非名字返回服务地址(端口号)
程序需要知道自己的主机名称,使用gethostname。它接受受一个缓冲区和缓冲区长度,并把运行
调用程序的主机的名称复制到给定的缓冲区。
#include <unistd.h>
int gethostname(char *namebuf, size_t buflength);
若缓冲区长度足够大,通过namebuf返回的字符串以null结尾。若缓冲区不够,则没有指定通过namebuf
返回的字符串是否以null结尾。
最大主机长度为HOST_NAME_MAX,不同主机有不同长度。hostname命令可以获取和设置主机名。
3.1.2 其它的地址查询
这些函数返回的网络配置信息可能存放在许多地方,例如:保存在静态文件中(如:/etc/
hosts, /etc/serviecs等),或者在命名服务管理(如:DNS, NIS(Network Information Service))。
这些函数都可以访问它们。
调用gethostent可以找到给定计算机的主机信息。
#include <netdb.h>
struct hostent *gethostent(void);
返回值: 成功返回指针,出错返回NULL
void sethostnet(int stayopen);
void endhostent(void);
如果主机数据文件没有打开,gethostent会打开它。函数gethostent返回文件的下一个条目。
函数sethostent会打开文件,如果文件已经被打开,那么将其回绕。函数endhostent关闭文件。
gethostent返回时,得到一个指向hostent结构的指针,该结构可能包含一个静态的数据缓冲区。
每次调用gethostent将会覆盖这个缓冲区。hostent至少包含:
struct hostent {
char *h_name; /* name of host */
char **h_aliases; /* pointer to alternate host name arry */
int h_addrtype; /* address type */
int h_length; /* length in bytes of address */
char **h_addr_list; /* pointer to array of network address */
};
返回的地址为网络字节顺序。