使用名字代替地址的原由

出于许多理由,我们应该使用名字而不是数值:名字比较容易记住;数值地址可以变动而名 字保持不变;随着往IPv6上转移,数值地址变得相当长,手工键入数值地址更易出错。本章讲 述在名字和数值地址间进行转换的函数:gethostbyname和gethostbyaddr在主机名字与IPv4 地址之间进行转换,getservbyname和getservbyport在服务名字和端口号之间进行转换。本 章还讲述两个协议无关的转换函数:getaddrinfo和getnameinfo,分别用于主机名字和IP地 址之间以及服务名字和端口号之间的转换。

域名系统

域名系统(Domain Name System, DNS)主要用于主机名字与IP地址之间的映射。主机名既可以是一个简单名字(simple name),例如Solaris或bsdi,也可以是一个全限定域名(Fully Qualified Domain Name, FQDN),例如Solaris.unpbook. com。

资源记录
DNS中的条目称为资源记录(resource record, RR)。我们感兴趣的RR类型只有若干个。
A A记录把一个主机名映射成一个32位的IPv4地址。举例来说,以下是 unpbook. com域中关于主机freebsd的4个DNS记录,其中第一个是一个A记录:
freebsd IN A 12.106.32.254
IN AAAA 3ffe:b80:If8d:1:a00:20ff:fea7:686b
IN MX 5 freebsd.unpbook.com.
IN MX 10 mailhost.unpbook. com.

AAAA 称为“四A”(quad A)记录的AAAA记录把一个主机名映射成一个128位的IPv6 地址。选择“四A”这个称呼是由于128位地址是32位地址的四倍。

PTR 称为“指针记录” (pointer record)的PTR记录把IP地址映射成主机名。对于IPv4 地址,32位地址的4字节先反转顺序,每字节都转换成各自的十进制ASCII值(0〜 255)后,再添上in-addr.arpa,结果字符串用于PTR查询。
对于IPv6地址,128位地址中的32个四位组先反转顺序,每个四位组都被转换成 相应的十六进制ASCII值(0〜9, a〜f)后,再添上ip6.arpa。
举例来说,上例中主机freebsd的两个PTR记录分别是254.32.106.12 . in- addr. arpa 和 b.6.8.6.7.a.e.f.f.f.0.2.0.0.a.0.1.0.0.0.d.8.f.l.0.8. b.O.e.f.f.3.ip6.arpa。

MX MX记录把一个主机指定作为给定主机的"邮件交换器"(mail
exchanger)。上例中主机freebsd有2个MX记录:第一个的优先级值为5,第二个的优先级值为10。 当存在多个MX记录时,它们按照优先级顺序使用,值越小优先级越高。

CNAME CNAME代表acanonical name"(规范名字),它的常见用法是为常用的服务(例 如ftp和www)指派CNAME记录。如果人们使用这些服务名而不是真实的主机 名,那么相应的服务挪到另一个主机时他们也不必知道。举例来说,我们名为Linux的主机有以下2个CNAME记录:
ftp IN CNAME linux. unpbook. com.
www IN CNAME linux.unpbook.com.

解析器和名字服务器

每个组织机构往往运行一个或多个名字服务器(name server),它们通常就是所谓的BIND(Berkeley Internet Name Domain的简称)程序。诸如我们在本书中编写的客户和服务器等应用程 序通过调用称为解析器(resolver)的函数库中的函数接触DNS服务器。常见的解析器函数是将在本章讲解的gethostbyname和gethostbyaddr,前者把主机名映射成IPv4地址,后者则执行 相反的映射。

图11.1展示了应用进程、解析器和名字服务器之间的一个典型关系。现在考虑编写应用程 序代码。解析器代码通常包含在一个系统函数库中,在构造应用程序时被链编(link-editing) 到应用程序中。另有些系统提供一个由全体应用进程共享的集中式解析器守护进程,并提供向 这个守护进程执行RPC的系统函数库代码。不论哪种情况,应用程序代码使用通常的函数调用来执行解析器中的代码,调用的典型函数是gethostbyname和gethostbyaddr

gethostbyname和gethostbyaddr函数,查找主机名

查找主机名最基本的函数是gethostbyname。如果调用成功,它就返回一个指向hostent 结构的指针,该结构中含有所查找主机的所有IPv4地址。这个函数的局限是只能返回IPv4地址。

#include<netdb.h>
struct hostent *gethostbyname(const char *hostname);返回值:若成功则为非空指针,若出错则为NULL且设置h_errnostruct hostent{char *h_name;     /*主机的官方(规范)名称*/char **h_ailases;    /*指向指向别名的指针数组的指针*/int h_addrtype;       /*主机地址类型:AF_INET */int h_length;        /*地址长度:4 */char **h_addr_list;  /*指向指向IPV4地址数组指针的指针 */
}


gethostbyname与我们介绍过的其他套接字函数的不同之处在于:当发生错误时,它不设置errno变量,而是将全局整数变量h_errno设置为在头文件<netdb. h>中定义的下列常值之一:
HOST_NOT_FOUND;
TRY_AGA工N;
NO_RECOVERY;
NO_DATA (等同于NO_ADDRESS)。

NO_DATA错误表示指定的名字有效,但是它没有A记录。只有MX记录的主机名就是这样的 一个例子。
如今多数解析器提供名为hstrerror的函数,它以某个h_errno值作为唯一的参数,返回的是一个const char *指针,指向相应错误的说明。

gethostbyaddr函数使用一个二进制的IP地址找到相应的主机名,与gethostbyname的行为刚好相反。

#include<netdb.h>struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);返回:若成功则为非空指针,若出错则为NULL且设置h_errno

addr参数实际上不是char *类型,而是一个指向存放IPv4地址的某个in_addr结构的指针; len参数是这个结构的大小:对于IPv4地址为4。family参数为AF_INET。

getservbyname和getservbyport函数,查找服务名

从名字到端口号的映射关系保存在一个文件中(通常是 /etc/services),那么即使端口号发生变动,我们需修改的仅仅是/etc/services文件中的某 一行,而不必重新编译应用程序。getservbyname函数用于根据给定名字查找相应服务。
赋予各个服务的端口号规范列表由IANA通过http://www.iana.org/assignments/port-num- bers维护。/etc/services文件通常包含IANA维护的规范赋值列表的某个子集。

#include<netdb.h>struct servent *getservbyname(const char *servname, const char *protoname);返回:若成功则为非空指针,若出错则为NULL
struct servent{char *s_name;        /*规范服务名称*/char **s_aliases; /*别名列表*/int s_port;         /*端口号,以网络字节序存储*/char *s_proto;      /*使用的协议*/
}

服务名参数servname必须指定。如果同时指定了协议(即protoname参数为非空指针),那么指定服务必须有匹配的协议。
servent结构中我们关心的主要字段是端口号。既然端口号是以网络字节序返回的,把它存放到套接字地址结构时绝对不能调用htons。

getsevbyport函数用于根据端口号和可选协议查找相应服务。

#include<netdb.h>struct servent *getservbyport(int port,const char *protoname);返回:若成功返回非空指针,若出错返回NULL

注意:port参数的值必须为网络字节序

getaddrinfo函数

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

#include<netdb.h>
int getaddrinfo(const char *hostname,const char *service,const struct addrinfo *hints,struct addrinfo **result);返回:若成功则为0,若出错则为非0
struct addrinfo{int     ai_flags;           /* AI_PASSIVE, AI_CANONNAME */
int     ai_family;          /* AF_xxx */
int     ai_socktype;        /* S0CK_xxx */
int     ai_protocol;        /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen;       /* ai_addr的长度* /
char    *ai_canonname;      /*ptr指向主机的规范名称*/
struct sockaddr *ai_addr;   /*ptr指向套接字地址结构*/
struct addrinfo*ai_next;    /*ptr指向链表中的下一个结构*/
}

其中hostname参数是一个主机名或地址串(IPv4的点分十进制数串或IPv6的十六进制数串)。service参数是一个服务名或十进制端口号数串。
hints参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说,如果指定的服务既支持TCP也支持UDP (例如指代某个DNS服务器的domain服务),那么调用者可以把hints结构中的ai_socktype成员 设置为SOCK_DGRAM,使得返回的仅仅是适用于数据报套接字的信息。
hints结构中调用者可以设置的成员有:
ai_flags (零个或多个或在一起的AI_xxx值);
ai_family (某个AF_xxx值);
ai_socktype (某个SOCK_xx值);
ai_protocol。

其中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_flag、ai_socktype和ai_protocol的值均为0, ai_family的值为AF_UNSPEC。
如果本函数返回成功(0),那么由result参数指向的变量已被填入一个指针,它指向的是由其中的ai_next成员串接起来的addrinfo结构链表。可导致返回多个addrinfo结构的情形有以下两个。
(1)如果与hostname参数关联的地址有多个,那么适用于所请求地址族(可通过如小结构的 ai_family成员设置)的每个地址都返回一个对应的结构。
(2)如果service参数指定的服务支持多个套接字类型,那么每个套接字类型都可能返回一个对应的结构,具体取决于hints结构的ai_socktype成员。
(注意,getaddrinfo的多数实现认为只能按照由ai_socktype成员请求的套接字类型端口号数串到端口的转换,如果没有指定这个成员,那就返回一个错误。)

举例来说,如果在没有提供任何暗示信息的前提下,请求查找有2个IP地址的某个主机上的 domain服务,那将返回4个addrinfo结构,分别是:
第一个IP地址组合SOCK_STREAM套接字类型;
第一个IP地址组合S OCK_DGRAM套接字类型;
第二个IP地址组合SOCK_STREAM套接字类型;
第二个IP地址组合SOCK_DGRAM套接字类型。

在addrinfo结构中返回的信息可现成用于socket调用,随后现成用于适合客户的connect 或sendto调用,或者是适合服务器的bind调用。

最安全的做法是让getaddrinfo总是返回明确的协议值。
图11.6汇总了根据指定的服务名(可以是一个十进制端口号数串)和ai_socktype暗示信息为每个通过主机名查找获得的IP地址返回addrinfo结构的数目。

典型的服务器进程只指定service而不指定hostname,同时在hints结构中指定AI_PASSIVE标志。返回的套接字地址结构中应含有一个值为INADDR_ANY (对于IPv4 )或 IN6ADDR_ANY_INIT(对于IPv6)的IP地址。TCP服务器随后调用socket、bind和listen。

gai_strerror函数

图11-7给出了可由getaddrinfo返回的非0错误值的名字和含义。gai_strerror以这些值为它的唯一参数,返回一个指向对应的出错信息串的指针。

#include<netdb.h>
const char *gai_strerror(int error);返回:指向错误描述消息字符串的指针

freeaddrinfo函数

getaddrinfo返回的所有存储空间都是动态获取的(譬如来自malloc调用),包括 addrinfo结构、ai_addr结构和ai_canonname字符串。这些存储空间通过调用freeaddrinfo 返还给系统。

#include<netdb.h>void freeaddrinfo(struct addrinfo *ai);

ai参数应指向由getaddrinfo返回的第一个addrinfo结构。这个链表中的所有结构以及由它们指向的任何动态存储空间(譬如套接字地址结构和规范主机名)都被释放掉。

图11-8汇总了getaddrinfo如何处理IPv4和IPv6地址。“结果” 一栏是在给定前三栏的变量后,该函数返回给调用者的结果。“行为” 一栏则说明该函数如何获取这些结果。

getnameinfo函数

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(见图11-7)

待返回的2个直观可读字符串由调用者预先分配存储空间。调用者不想返回某一个值,则将对应的len置0。
图11-20中给出了6个可指定的标志,用于改变getnameinfo的操作。

NI_NOFQDN标志导致返回的主机名第一个点号之后的内容被截去。
对于这些标志有意义的组合(例如NI_DGRAM和NI_NUMERICHOST),可以把其中各个标志逻辑或在一起。

来源:UNIX网络编程卷一第十一章

服务器规范名字与IP地址的转换相关推荐

  1. 一个网站服务器有多少个ip,一个服务器可以有多少个ip地址

    一个服务器可以有多少个ip地址 内容精选 换一换 华为云帮助中心,为用户提供产品简介.价格说明.购买指南.用户指南.API参考.最佳实践.常见问题.视频帮助等技术文档,帮助您快速上手使用华为云服务. ...

  2. android 整数与ip地址的转换

    今天,简单讲讲android里如何把整数与ip地址进行转换. 这个很简单,在网上查找资料就找到了.这里记录一下. 1.IP地址转换为整数 将IP地址转化成整数的方法如下:      1.通过Strin ...

  3. 两台服务器虚拟成一个ip地址,两台服务器如何用一个ip地址

    两台服务器如何用一个ip地址 内容精选 换一换 华为云帮助中心,为用户提供产品简介.价格说明.购买指南.用户指南.API参考.最佳实践.常见问题.视频帮助等技术文档,帮助您快速上手使用华为云服务. 检 ...

  4. centos7修改ip地址自动获取_南京课工场IT培训:如何搭建DHCP服务器及自动获取IP地址及相关操作...

    DHCP服务相关理论知识 DHCP服务分为以下三点, IP地址(例:192.168.1.0) 子网掩码(例:255.255.255.0) 网关 DHCP服务的好处: 减少管理员的工作量 避免输入错误的 ...

  5. 数据库修改服务器ip地址吗,服务器数据库与改ip地址吗

    服务器数据库与改ip地址吗 内容精选 换一换 云数据库 GaussDB NoSQL与其他服务的关系,如图1所示. 安全组的默认规则是在出方向上的数据报文全部放行,同一个安全组内的弹性云服务器和数据库实 ...

  6. 服务器上登录网页ip地址,查看服务器上登录的ip地址

    查看服务器上登录的ip地址 内容精选 换一换 确认虚拟机主网卡已经正确分配到IP地址.登录虚拟机内部.执行ifconfig命令或ip address查看网卡的IP信息.Windows虚拟机可以在命令行 ...

  7. 虚拟服务器ip怎么配,如何向虚拟服务器分配端口和 IP 地址

    如何向虚拟服务器分配端口和 IP 地址 10/25/2013 本文内容 上一次修改主题: 2005-05-18 为某个协议创建虚拟服务器时,可以为该服务器选择使用默认分配的端口和 IP(Interne ...

  8. 在dhcp服务器上新建一个作用域需要定,在DHCP服务器上创建一个IP地址作用域,它的名称为“offi - 问答库...

    问题: [填空题] 在DHCP服务器上创建一个IP地址作用域,它的名称为"office",所包含的IP地址范围为:192.168.1.1-192.168.1.100,子网掩码为:2 ...

  9. 怎么绑定虚拟服务器,虚拟服务器里怎样绑定ip地址

    虚拟服务器里怎样绑定ip地址 内容精选 换一换 在高可用部署场景下,ASCS主备节点通过共享盘实现数据同步.本章节指导用户将ASCS主节点的数据盘绑定给ASCS备节点并为ASCS主备节点绑定浮动IP. ...

最新文章

  1. SpringBoot配置文件YAML配置注入(详解)
  2. shell中大于、等于、小于
  3. tomcat高并发配置调优
  4. python有道词典-利用Python3和Charles爬取有道词典
  5. Ubuntu配置静态ip联网总结
  6. JS获取页面元素并修改
  7. std string与线程安全,是std :: regex线程安全吗?
  8. 剧情很有趣:安全专家被骗记
  9. [导入]DotText源码阅读(2)-工程、数据库表结构
  10. C++0x 通用属性
  11. hadoop2.7的目录结构
  12. 【LeetCode】寻找两个有序数组的中位数【性质分析+二分】
  13. android 高德地图设置不能旋转_如何将平面控制点导入Google Earth、奥维互动地图及手机奥维互动地图APP里面?...
  14. android autolayout教程,Android中AutoLayout的使用
  15. 单例模式-序列号生成器
  16. 手机端车牌识别软件下载
  17. RDD转换为DataFrame的两种方式详解
  18. 【超详细】嵌入式软件学习大纲
  19. aliyun - ddns
  20. python 'NoneType' object has no attribute '_root'

热门文章

  1. python循环结构教学设计_Python程序设计 循环结构说课稿
  2. Windows10搭建turn服务器
  3. python-pandapower电力系统短路电流计算(算例3:探索一天的最佳电网运行方式))
  4. 【Demo】银行主数据的修改
  5. android 高德地图 定位蓝点消失,高德地图蓝点定位不显示蓝点
  6. 新增数学与人工智能学部,考数据结构!齐鲁工业大学(山东省科学院)计算机考研...
  7. vue实现下拉二级联动_vue实现二级联动效果
  8. ajax实现数据库获取select二级联动下拉菜单
  9. html省市多级联动下拉框,基于javascript实现全国省市二级联动下拉选择菜单
  10. FFMPEG 实现混音,加水印,加文字,模糊水印任意滤镜组合