「Linux 网络编程」DNS 域名解析、域名与 IP 地址的转换
1 域名
1.1 什么是域名
域名(Domain Name)是一串可读的文本(例如 www.deepseek.com、openai.com),用于在互联网上 标识某台主机或服务。域名为人类提供了友好的命名方式 —— 比起难记的数字地址(如 93.184.216.34),域名更易于识别与记忆。
1.2 域名的作用
- 易记:便于用户访问(例如输入
google.com)。 - 抽象:把具体服务器地址(IP)与服务分离,便于迁移或负载管理。
- 管理:可以为不同子域设置不同服务(
www.,mail.,api.等)。
1.3 域名的层次结构
域名是分层的,最右侧是顶级域(TLD,例如 .com, .cn);向左逐层是二级域、三级域等(例如 www 是三级域)。DNS 按层次逐级解析。
以 www.google.com 为例,对这个域名进行层次分解:
-
根域
- 位置:层次结构的最高点,在域名的最右边,通常被省略不写。
- 表示:用一个点
.来表示。例如,www.google.com.最后的那个点就是根域。在大多数日常使用中,不需要输入这个点,浏览器会自动补全。 - 作用:根域是 DNS 查询的起点。全球有13组根域名服务器,它们知道所有顶级域名的信息。
-
顶级域
- 位置:紧靠在根域的左边,是域名的最后一部分。
- 例子:
.com,.org,.net,.cn,.uk等。 - 作用:TLD 对域名进行分类。它主要分为两类:
- 通用顶级域:如
.com(商业机构)、.org(非营利组织)、.net(网络机构)、.edu(教育机构)等。 - 国家/地区顶级域:如
.cn(中国)、.uk(英国)、.jp(日本)等。
- 通用顶级域:如
在例子
www.google.com中,.com就是顶级域。 -
二级域
- 位置:在顶级域的左边。
- 例子:
google.com中的google就是二级域。 - 作用:这是向域名注册商 注册的、独一无二的 的名称。代表了组织、品牌或项目。例如
baidu.com,microsoft.com中的baidu和microsoft都是二级域。
-
子域
- 位置:在二级域的左边,是可选的。
- 例子:
www.google.com中的www就是子域。 - 作用:二级域的所有者可以自由创建子域,用来将网站或服务划分为不同的部分。常见的子域有:
www:通常指向网站的主服务器。mail:可能指向邮件服务器。blog:可能指向博客站点。shop:可能指向在线商店。
可以创建任意名称的子域,例如
drive.google.com或maps.google.com。
2 DNS 域名系统
2.1 DNS 解决的问题
在 DNS(Domain Name System) 诞生之前,互联网主要依靠一个名为 “HOSTS.TXT” 的中央文件来进行主机名到 IP 地址的映射。所有联网的计算机都需要定期从中央服务器下载并更新这个文件。
随着网络规模扩大,这种机制带来了 3 个难以克服的问题:
-
难以维护
-
单点瓶颈和更新压力:任何一台新主机接入网络,或者任何一台现有主机更改 IP 地址都需要向中央服务器发起请求,然后等待中央服务器更新那个唯一的 “HOST.TXT” 的文件。随着主机数量的不断增加,中央服务器的更新压力无疑是很大的。
-
巨大的流量消耗:全球成千上万台计算机,每天甚至每小时,都需要连接到同一台服务器,重复下载一个越来越庞大的文件。这对中央服务器造成了巨大的流量压力,也浪费了大量的网络带宽。
-
数据不一致:数据同步不是实时的,总有一些机器还没来得及下载最新的版本。用户可能根据自己机器上过时的 HOSTS 文件,尝试访问一个已经变更的 IP 地址,导致连接失败。
就像在手机通讯录出现之前,所有人都必须每天去市政厅抄写一份最新的全市居民电话号码本。市政厅忙得不可开交,全市的交通也都被去抄号码本的人堵死了,而且你拿到的版本还可能是昨天的。
-
-
缺乏唯一性(命名冲突和混乱)
-
命名的局限性: HOSTS 文件是一个扁平的命名空间,无法支持层次化的结构。这意味着所有主机名都必须是全局唯一的。
-
无法避免命名冲突:当两个不同的组织(比如一家公司和一所大学)都想用自己的邮件服务器主机名 “mail” 时,就产生了命名冲突。后注册的机构将无法使用这个简单直观的名字,必须被迫改为 “mail2”、“our_mail” 等不直观的名称。
就像在一个只能使用“小黑”、“小黄”作为称呼的学校里,一旦有两个“小黑”,老师点名时就会一片混乱。而 DNS 引入了姓氏和家庭住址的层次概念(如
mail.google.com和mail.harvard.edu),完美地解决了这个问题。 -
-
不够灵活(变更的延迟)
-
僵化的映射关系:IP 地址与主机名的绑定关系被“写死”在每台电脑的本地 HOSTS 文件里。这是一个 静态的、非动态 的映射。
-
变更无法及时生效:如果一台服务器因为维护、扩容或故障需要更换 IP 地址,那么从它更换 IP 的那一刻起,到全球最后一台计算机更新其 HOSTS 文件为止的这段漫长时期内,所有仍在使用旧 HOSTS 文件的用户都将无法访问该服务。这个延迟可能是数小时甚至数天,这显然难以接受。
-
无法实现高级功能:基于 HOSTS 文件的方式,根本 无法负载均衡(将一个域名指向多个IP)或故障转移(一个服务器宕机自动切换到备用服务器)这样的高级网络功能。
这就像一家搬了家的公司,它需要亲自通知到全世界每一个潜在的客户。在通知完所有人之前,总会有人跑到它的旧地址去,发现已经人去楼空。而 DNS 就像一个实时更新的“114查号台”,公司只需在查号台更新自己的新地址,所有打电话查询的人立刻就能得到最新、最准确的信息。
-
2.2 DNS 的核心组成
DNS 主要由 DNS 递归解析器、根域名服务器、顶级域名服务器、权威域名服务器 组成。
-
DNS 递归解析器(Recursive Resolver)
面向客户端的 服务代理。
功能:DNS 递归解析器接收来自客户端(电脑、手机)的查询请求,并代表客户端 进行整个查询流程。包括向后续的各级服务器发起域名查询、获得对应的 IP 地址、最终将获取的 IP 地址返回给客户端。
特点:通常由 互联网服务提供商(ISP)或 公共 DNS 服务(如
223.5.5.5,8.8.8.8,1.1.1.1)提供。具备 缓存功能,以提升查询效率。 -
根域名服务器(Root DNS)
DNS 查询的 “起点”。
功能:不存储具体域名的 IP 地址,但存储了所有 顶级域(TLD)服务器的地址。当递归解析器向它查询时,其 返回一个指向对应 TLD 服务器的指引。
特点:全球共有 13 组逻辑根服务器集群(从A到M),但通过任播技术,每个集群都在全球有数百个物理镜像节点,从而构成了一个庞大且稳健的网络。
-
顶级域名服务器(TLD DNS)
管理特定顶级域的 “区域管理员”。
功能:负责管理其下所有的二级域名。例如,
.comTLD服务器 知道所有以.com结尾的域名(如google.com,deepseek.com)是 由哪台权威服务器(Authoritative DNS)管理。当递归解析器向它查询时,其返回对应域名的权威服务器地址。 -
权威域名服务器(Authoritative DNS)
域名记录的 “档案管理员”。
功能:是域名所有者(或其托管商)维护的服务器,最终负责存储并提供该域名下的所有 DNS 记录(如 A 记录、AAAA 记录、MX 记录等)。当递归解析器最终查询到它时,会返回所请求域名的确切 IP 地址。
特点:一个域名通常有至少两台权威服务器(主和备)以确保高可用性。
2.3 DNS 域名解析过程
以查询 www.example.com 为例:

-
接收请求
在浏览器输入
www.example.com并按回车后,一开始会在浏览器 DNS 缓存中查询是否有对应的地址,然后会查看操作系统缓存及 Host 文件。如果这些缓存中都没有找到,操作系统会将查询请求发送到预先配置好的 本地 DNS 服务器。 -
查询本地 DNS 服务器缓存
本地 DNS 服务器收到请求后,首先不会立即向外查询,而是 检查自己的缓存数据库。
-
如果缓存中存在且未过期:
直接返回对应的 IP 地址给客户端。这个过程在毫秒级内完成,解析结束。
-
如果缓存中不存在(或已过期)
本地 DNS 服务器将启动一次 完整的递归查询 过程。
-
-
执行递归/迭代查询
本地 DNS 服务器此时扮演客户端的“全权代理”。这个过程通常是它自己进行迭代查询。
-
询问根服务器:它向全球 13 组根服务器之一发起查询。根服务器不给出答案,但返回一个指引:“我不知道
www.example.com,但你可以去问负责.com域的 顶级域(TLD)服务器。”(返回 TLD 服务器的地址) -
询问 TLD 服务器:本地 DNS 服务器接着向其中一个
.comTLD 服务器发起查询。TLD 服务器同样不给出最终答案,但返回另一个指引:“我不知道www.example.com,但example.com这个域归那台 权威服务器 管理。”(返回对应权威域名服务器的地址) -
询问权威服务器:最后,本地 DNS 服务器向
example.com的权威域名服务器发起查询。权威服务器是记录的“最终源头”,它会返回www.example.com对应的 A记录 或 AAAA记录,也就是 最终的IP地址。
-
-
缓存并返回结果
缓存答案:本地 DNS 服务器在拿到 IP 地址后,会将这个结果 存储在自己的缓存中。存储时间由记录中的 TTL 值决定。为后续所有向它查询相同域名的用户提供极快的响应。
返回客户端:将最终的 IP 地址返回给操作系统,再由操作系统传递给浏览器。
-
客户端建立连接
浏览器拿到 IP 地址后,终于可以与
www.example.com的服务器建立 TCP 连接,发送 HTTP 请求,渲染并加载网页内容。
2.4 常见的 DNS 记录类型
- A:IPv4 地址(例如
93.184.216.34)。 - AAAA:IPv6 地址(例如
2606:2800:220:1:248:1893:25c8:1946)。 - CNAME:别名记录,把一个域名指向另一个域名(例如
www.example.com→example.com)。 - MX:邮件交换记录,指定邮件服务器。
- PTR:指针记录,用于反向解析(IP → 域名)。
- NS:指定某个域名的权威 DNS 服务器。
- TXT:文本记录(常用于 SPF、DKIM、验证等)。
3 IP 与域名的转换(常用命令)
3.1 正向解析(域名 → IP)
常用命令:
-
dig example.com A或dig example.com AAAA—— 灵活,显示完整 DNS 响应。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23; <<>> DiG 9.11.3-1ubuntu1.18-Ubuntu <<>> example.com A
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51645
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;example.com. IN A
;; ANSWER SECTION:
example.com. 5 IN A 23.192.228.80
example.com. 5 IN A 23.215.0.138
example.com. 5 IN A 23.192.228.84
example.com. 5 IN A 23.220.75.232
example.com. 5 IN A 23.220.75.245
example.com. 5 IN A 23.215.0.136
;; Query time: 8 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Fri Oct 10 15:54:04 CST 2025
;; MSG SIZE rcvd: 136 -
host example.com—— 直接显示 IP。1
2
3
4
5
6
7
8
9
10
11
12
13example.com has address 23.215.0.138
example.com has address 23.192.228.84
example.com has address 23.220.75.232
example.com has address 23.220.75.245
example.com has address 23.215.0.136
example.com has address 23.192.228.80
example.com has IPv6 address 2600:1406:bc00:53::b81e:94ce
example.com has IPv6 address 2600:1408:ec00:36::1736:7f24
example.com has IPv6 address 2600:1406:5e00:6::17ce:bc12
example.com has IPv6 address 2600:1406:bc00:53::b81e:94c8
example.com has IPv6 address 2600:1408:ec00:36::1736:7f31
example.com has IPv6 address 2600:1406:5e00:6::17ce:bc1b
example.com mail is handled by 0 . -
nslookup example.com—— 交互式/简单查询(逐步被dig/host取代)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
Name: example.com
Address: 23.220.75.232
Name: example.com
Address: 23.220.75.245
Name: example.com
Address: 23.215.0.136
Name: example.com
Address: 23.192.228.80
Name: example.com
Address: 23.215.0.138
Name: example.com
Address: 23.192.228.84
Name: example.com
Address: 2600:1406:bc00:53::b81e:94ce
Name: example.com
Address: 2600:1408:ec00:36::1736:7f24 -
ping example.com/ping6—— 实际向主机发送 ICMP(也会先解析域名)。1
2
3
4
5
6
7
8
9
10
11
12PING example.com (23.192.228.84) 56(84) bytes of data.
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=1 ttl=128 time=164 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=2 ttl=128 time=164 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=3 ttl=128 time=164 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=4 ttl=128 time=163 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=5 ttl=128 time=161 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=7 ttl=128 time=161 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=9 ttl=128 time=161 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=11 ttl=128 time=164 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=13 ttl=128 time=167 ms
64 bytes from 23.192.228.84 (23.192.228.84): icmp_seq=14 ttl=128 time=166 ms
...
3.2 反向解析(IP → 域名)
-
dig -x 60.204.2.4—— 查询 PTR 记录。如果反向解析不成功,可以尝试更换指定的公共 DNS,例如
8.8.8.8。命令改为dig -x 60.204.2.4 @8.8.8.8。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18; <<>> DiG 9.11.3-1ubuntu1.18-Ubuntu <<>> -x 60.204.2.4 @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63863
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;4.2.204.60.in-addr.arpa. IN PTR
;; ANSWER SECTION:
4.2.204.60.in-addr.arpa. 112 IN PTR ecs-60-204-2-4.compute.hwclouds-dns.com.
;; Query time: 186 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Fri Oct 10 16:08:13 CST 2025
;; MSG SIZE rcvd: 105 -
host 60.204.2.4—— 会尝试反向查找。如果反向解析不成功,可以尝试更换指定的公共 DNS,例如
8.8.8.8。命令改为host 60.204.2.4 8.8.8.8。1
2
3
4
5
6Using domain server:
Name: 8.8.8.8
Address: 8.8.8.8#53
Aliases:
4.2.204.60.in-addr.arpa domain name pointer ecs-60-204-2-4.compute.hwclouds-dns.com.
注意:并非所有 IP 都有 PTR 记录;有时反向解析返回空或返回 ISP/宿主单位的默认 PTR。反向解析依赖于 IP 的 拥有者/ISP 在对应的 in-addr.arpa 或 ip6.arpa 区域里设置 PTR。
4 Linux 系统函数实现域名和 IP 的转换
现代推荐使用 getaddrinfo(域名到 socket 地址)和 getnameinfo(socket 地址到主机名)。这两者是可移植、支持 IPv4/IPv6、线程安全的现代接口。旧接口 gethostbyname / gethostbyaddr 已过时(非线程安全),只在遗留代码中看到。
4.1 getaddrinfo / freeaddrinfo
函数原型:
1 | |
返回值:0 表示成功;非 0 表示错误(可用 gai_strerror() 转为字符串)。
常用字段(struct addrinfo):
1 | |
代码示例(域名 → IP,支持 IPv4/IPv6):
1 | |
编译运行:
1 | |
运行结果:
1 | |
4.2 getnameinfo
将 struct sockaddr(例如来自 accept() 或 getaddrinfo)转换为主机名与服务名(反向解析)。
函数原型:
1 | |
返回值:0 成功;非 0 使用 gai_strerror 获取错误信息。
常用 flags:
NI_NAMEREQD:需要名字(否则报错);默认可返回数字地址字符串。NI_NUMERICHOST/NI_NUMERICSERV:返回数字形式(跳过 DNS/服务名解析)。
代码示例(IP → 域名,反向解析):
1 | |
编译运行:
1 | |
运行结果:
如果反向解析失败,可以 sudo vim /etc/resolv.conf,改为 nameserver 8.8.8.8 或者 nameserver 1.1.1.1。
1 | |
4.3 旧接口
struct hostent *gethostbyname(const char *name);struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
这些接口返回 struct hostent,但 不是线程安全(会返回静态缓冲区),并且在面对 IPv6/多记录情况时不够灵活。现代程序应使用 getaddrinfo/getnameinfo。若必须在多线程中使用遗留接口,可考虑 gethostbyname_r,但推荐迁移到 POSIX 的 getaddrinfo。
4.4 Linux DNS 解析流程
1 | |