「Linux 网络编程」DNS 域名解析、域名与 IP 地址的转换

1 域名

1.1 什么是域名

域名(Domain Name)是一串可读的文本(例如 www.deepseek.comopenai.com),用于在互联网上 标识某台主机或服务。域名为人类提供了友好的命名方式 —— 比起难记的数字地址(如 93.184.216.34),域名更易于识别与记忆。


1.2 域名的作用

  • 易记:便于用户访问(例如输入 google.com)。
  • 抽象:把具体服务器地址(IP)与服务分离,便于迁移或负载管理。
  • 管理:可以为不同子域设置不同服务(www., mail., api. 等)。

1.3 域名的层次结构

域名是分层的,最右侧是顶级域(TLD,例如 .com, .cn);向左逐层是二级域、三级域等(例如 www 是三级域)。DNS 按层次逐级解析。

www.google.com 为例,对这个域名进行层次分解:

  1. 根域

    • 位置:层次结构的最高点,在域名的最右边,通常被省略不写。
    • 表示:用一个点 . 来表示。例如,www.google.com. 最后的那个点就是根域。在大多数日常使用中,不需要输入这个点,浏览器会自动补全。
    • 作用根域是 DNS 查询的起点。全球有13组根域名服务器,它们知道所有顶级域名的信息。
  2. 顶级域

    • 位置:紧靠在根域的左边,是域名的最后一部分。
    • 例子.com, .org, .net, .cn, .uk 等。
    • 作用:TLD 对域名进行分类。它主要分为两类:
      • 通用顶级域:如 .com(商业机构)、.org(非营利组织)、.net(网络机构)、.edu(教育机构)等。
      • 国家/地区顶级域:如 .cn(中国)、.uk(英国)、.jp(日本)等。

    在例子 www.google.com 中,.com 就是顶级域。

  3. 二级域

    • 位置:在顶级域的左边。
    • 例子google.com 中的 google 就是二级域。
    • 作用:这是向域名注册商 注册的、独一无二的 的名称。代表了组织、品牌或项目。例如 baidu.com, microsoft.com 中的 baidumicrosoft 都是二级域。
  4. 子域

    • 位置:在二级域的左边,是可选的。
    • 例子www.google.com 中的 www 就是子域。
    • 作用:二级域的所有者可以自由创建子域,用来将网站或服务划分为不同的部分。常见的子域有:
      • www:通常指向网站的主服务器。
      • mail:可能指向邮件服务器。
      • blog:可能指向博客站点。
      • shop:可能指向在线商店。

    可以创建任意名称的子域,例如 drive.google.commaps.google.com



2 DNS 域名系统

2.1 DNS 解决的问题

DNS(Domain Name System) 诞生之前,互联网主要依靠一个名为 “HOSTS.TXT” 的中央文件来进行主机名到 IP 地址的映射。所有联网的计算机都需要定期从中央服务器下载并更新这个文件。

随着网络规模扩大,这种机制带来了 3 个难以克服的问题:

  1. 难以维护

    • 单点瓶颈和更新压力:任何一台新主机接入网络,或者任何一台现有主机更改 IP 地址都需要向中央服务器发起请求,然后等待中央服务器更新那个唯一的 “HOST.TXT” 的文件。随着主机数量的不断增加,中央服务器的更新压力无疑是很大的。

    • 巨大的流量消耗:全球成千上万台计算机,每天甚至每小时,都需要连接到同一台服务器,重复下载一个越来越庞大的文件。这对中央服务器造成了巨大的流量压力,也浪费了大量的网络带宽。

    • 数据不一致:数据同步不是实时的,总有一些机器还没来得及下载最新的版本。用户可能根据自己机器上过时的 HOSTS 文件,尝试访问一个已经变更的 IP 地址,导致连接失败。

    就像在手机通讯录出现之前,所有人都必须每天去市政厅抄写一份最新的全市居民电话号码本。市政厅忙得不可开交,全市的交通也都被去抄号码本的人堵死了,而且你拿到的版本还可能是昨天的。

  2. 缺乏唯一性(命名冲突和混乱)

    • 命名的局限性: HOSTS 文件是一个扁平的命名空间,无法支持层次化的结构。这意味着所有主机名都必须是全局唯一的。

    • 无法避免命名冲突:当两个不同的组织(比如一家公司和一所大学)都想用自己的邮件服务器主机名 “mail” 时,就产生了命名冲突。后注册的机构将无法使用这个简单直观的名字,必须被迫改为 “mail2”、“our_mail” 等不直观的名称。

    就像在一个只能使用“小黑”、“小黄”作为称呼的学校里,一旦有两个“小黑”,老师点名时就会一片混乱。而 DNS 引入了姓氏和家庭住址的层次概念(如 mail.google.commail.harvard.edu),完美地解决了这个问题。

  3. 不够灵活(变更的延迟)

    • 僵化的映射关系:IP 地址与主机名的绑定关系被“写死”在每台电脑的本地 HOSTS 文件里。这是一个 静态的、非动态 的映射。

    • 变更无法及时生效:如果一台服务器因为维护、扩容或故障需要更换 IP 地址,那么从它更换 IP 的那一刻起,到全球最后一台计算机更新其 HOSTS 文件为止的这段漫长时期内,所有仍在使用旧 HOSTS 文件的用户都将无法访问该服务。这个延迟可能是数小时甚至数天,这显然难以接受。

    • 无法实现高级功能:基于 HOSTS 文件的方式,根本 无法负载均衡(将一个域名指向多个IP)或故障转移(一个服务器宕机自动切换到备用服务器)这样的高级网络功能。

    这就像一家搬了家的公司,它需要亲自通知到全世界每一个潜在的客户。在通知完所有人之前,总会有人跑到它的旧地址去,发现已经人去楼空。而 DNS 就像一个实时更新的“114查号台”,公司只需在查号台更新自己的新地址,所有打电话查询的人立刻就能得到最新、最准确的信息。


2.2 DNS 的核心组成

DNS 主要由 DNS 递归解析器根域名服务器顶级域名服务器权威域名服务器 组成。

  1. DNS 递归解析器(Recursive Resolver)

    面向客户端的 服务代理

    功能:DNS 递归解析器接收来自客户端(电脑、手机)的查询请求,并代表客户端 进行整个查询流程。包括向后续的各级服务器发起域名查询、获得对应的 IP 地址、最终将获取的 IP 地址返回给客户端。

    特点:通常由 互联网服务提供商(ISP)或 公共 DNS 服务(如 223.5.5.5, 8.8.8.8, 1.1.1.1)提供。具备 缓存功能,以提升查询效率。

  2. 根域名服务器(Root DNS)

    DNS 查询的 “起点”

    功能:不存储具体域名的 IP 地址,但存储了所有 顶级域(TLD)服务器的地址。当递归解析器向它查询时,其 返回一个指向对应 TLD 服务器的指引

    特点:全球共有 13 组逻辑根服务器集群(从A到M),但通过任播技术,每个集群都在全球有数百个物理镜像节点,从而构成了一个庞大且稳健的网络。

  3. 顶级域名服务器(TLD DNS)

    管理特定顶级域的 “区域管理员”

    功能:负责管理其下所有的二级域名。例如,.com TLD服务器 知道所有以 .com 结尾的域名(如 google.com, deepseek.com)是 由哪台权威服务器(Authoritative DNS)管理。当递归解析器向它查询时,其返回对应域名的权威服务器地址。

  4. 权威域名服务器(Authoritative DNS)

    域名记录的 “档案管理员”

    功能:是域名所有者(或其托管商)维护的服务器,最终负责存储并提供该域名下的所有 DNS 记录(如 A 记录、AAAA 记录、MX 记录等)。当递归解析器最终查询到它时,会返回所请求域名的确切 IP 地址

    特点:一个域名通常有至少两台权威服务器(主和备)以确保高可用性。



2.3 DNS 域名解析过程

以查询 www.example.com 为例:

DNS 域名解析过程

  1. 接收请求

    在浏览器输入 www.example.com 并按回车后,一开始会在浏览器 DNS 缓存中查询是否有对应的地址,然后会查看操作系统缓存及 Host 文件。如果这些缓存中都没有找到,操作系统会将查询请求发送到预先配置好的 本地 DNS 服务器

  2. 查询本地 DNS 服务器缓存

    本地 DNS 服务器收到请求后,首先不会立即向外查询,而是 检查自己的缓存数据库

    • 如果缓存中存在且未过期

      直接返回对应的 IP 地址给客户端。这个过程在毫秒级内完成,解析结束。

    • 如果缓存中不存在(或已过期)

      本地 DNS 服务器将启动一次 完整的递归查询 过程。

  3. 执行递归/迭代查询

    本地 DNS 服务器此时扮演客户端的“全权代理”。这个过程通常是它自己进行迭代查询。

    1. 询问根服务器:它向全球 13 组根服务器之一发起查询。根服务器不给出答案,但返回一个指引:“我不知道 www.example.com,但你可以去问负责 .com 域的 顶级域(TLD)服务器。”(返回 TLD 服务器的地址)

    2. 询问 TLD 服务器:本地 DNS 服务器接着向其中一个 .com TLD 服务器发起查询。TLD 服务器同样不给出最终答案,但返回另一个指引:“我不知道 www.example.com,但 example.com 这个域归那台 权威服务器 管理。”(返回对应权威域名服务器的地址)

    3. 询问权威服务器:最后,本地 DNS 服务器向 example.com 的权威域名服务器发起查询。权威服务器是记录的“最终源头”,它会返回 www.example.com 对应的 A记录 或 AAAA记录,也就是 最终的IP地址

  4. 缓存并返回结果

    缓存答案:本地 DNS 服务器在拿到 IP 地址后,会将这个结果 存储在自己的缓存中。存储时间由记录中的 TTL 值决定。为后续所有向它查询相同域名的用户提供极快的响应。

    返回客户端:将最终的 IP 地址返回给操作系统,再由操作系统传递给浏览器。

  5. 客户端建立连接

    浏览器拿到 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.comexample.com)。
  • MX:邮件交换记录,指定邮件服务器。
  • PTR:指针记录,用于反向解析(IP → 域名)。
  • NS:指定某个域名的权威 DNS 服务器。
  • TXT:文本记录(常用于 SPF、DKIM、验证等)。


3 IP 与域名的转换(常用命令)

3.1 正向解析(域名 → IP)

常用命令:

  • dig example.com Adig 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
    13
    example.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
    20
    Server:		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
    12
    PING 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
    6
    Using 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.arpaip6.arpa 区域里设置 PTR。



4 Linux 系统函数实现域名和 IP 的转换

现代推荐使用 getaddrinfo(域名到 socket 地址)和 getnameinfo(socket 地址到主机名)。这两者是可移植、支持 IPv4/IPv6、线程安全的现代接口。旧接口 gethostbyname / gethostbyaddr 已过时(非线程安全),只在遗留代码中看到。


4.1 getaddrinfo / freeaddrinfo

函数原型

1
2
3
4
5
6
7
8
9
10
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *node, // 主机名(域名)或数字字符串("127.0.0.1" / "::1")
const char *service, // 服务名或端口号字符串("http" 或 "80")
const struct addrinfo *hints, // 可选,指定希望的地址族/套接字类型等
struct addrinfo **res); // 输出,结果链表

void freeaddrinfo(struct addrinfo *res);

返回值:0 表示成功;非 0 表示错误(可用 gai_strerror() 转为字符串)。

常用字段(struct addrinfo)

1
2
3
4
5
6
7
8
9
10
struct addrinfo {
int ai_flags;
int ai_family; // AF_INET / AF_INET6 / AF_UNSPEC
int ai_socktype; // SOCK_STREAM / SOCK_DGRAM / 0
int ai_protocol; // IPPROTO_TCP / IPPROTO_UDP / 0
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};

代码示例(域名 → IP,支持 IPv4/IPv6):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// resolve.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n", argv[0]);
return 1;
}
const char *hostname = argv[1]; // 需要解析的域名
struct addrinfo hints; // 指定希望的地址族/套接字类型等
struct addrinfo* res; // 返回的 ip 地址列表
char ipstr[INET6_ADDRSTRLEN];

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6,满足 IPv4 和 IPv6
hints.ai_socktype = SOCK_STREAM;

int val = getaddrinfo(hostname, NULL, &hints, &res);
if (val != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(val));
return 2;
}

printf("IP addresses for %s:\n\n", hostname);
struct addrinfo* p; // 遍历 ip 地址链表的指针
for (p = res; p != NULL; p = p->ai_next) {
void* addr; // 存储 IP 地址二进制格式
const char* ipver; // IP 地址类型,IPv4 or IPv6

if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in* ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); // IP地址 二进制转换为字符串格式
printf(" %s: %s\n", ipver, ipstr);
}

freeaddrinfo(res); // 释放空间

return 0;
}

编译运行

1
2
gcc resolve.c -o resolve 
./resolve example.com

运行结果

1
2
3
4
5
6
7
8
9
10
IP addresses for example.com:

IPv4: 23.215.0.136
IPv4: 23.192.228.80
IPv4: 23.215.0.138
IPv4: 23.192.228.84
IPv4: 23.220.75.232
IPv4: 23.220.75.245
IPv6: 2600:1406:bc00:53::b81e:94ce
IPv6: 2600:1408:ec00:36::1736:7f24

4.2 getnameinfo

struct sockaddr(例如来自 accept()getaddrinfo)转换为主机名与服务名(反向解析)。

函数原型

1
2
3
4
5
6
7
#include <sys/socket.h>
#include <netdb.h>

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen,
int flags);

返回值:0 成功;非 0 使用 gai_strerror 获取错误信息。

常用 flags

  • NI_NAMEREQD:需要名字(否则报错);默认可返回数字地址字符串。
  • NI_NUMERICHOST / NI_NUMERICSERV:返回数字形式(跳过 DNS/服务名解析)。

代码示例(IP → 域名,反向解析):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// reverse.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>

int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s ip-address\n", argv[0]);
return 1;
}
const char* ip = argv[1];
struct sockaddr_storage sa;
socklen_t sa_len;
char host[NI_MAXHOST];

// 判断 IPv4 或 IPv6
if (strchr(ip, ':')) { // 有冒号,可能 IPv6
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&sa;
sa6->sin6_family = AF_INET6;
inet_pton(AF_INET6, ip, &(sa6->sin6_addr));
sa_len = sizeof(struct sockaddr_in6);
} else {
struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa;
sa4->sin_family = AF_INET;
inet_pton(AF_INET, ip, &(sa4->sin_addr));
sa_len = sizeof(struct sockaddr_in);
}

int rc = getnameinfo((struct sockaddr*)&sa, sa_len, host, sizeof host, NULL, 0, NI_NAMEREQD);
if (rc != 0) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(rc));
return 2;
}
printf("Reverse lookup: %s -> %s\n", ip, host);

return 0;
}

编译运行

1
2
gcc reverse.c -o reverse 
./reverse 60.204.2.4

运行结果

如果反向解析失败,可以 sudo vim /etc/resolv.conf,改为 nameserver 8.8.8.8 或者 nameserver 1.1.1.1

1
Reverse lookup: 60.204.2.4 -> ecs-60-204-2-4.compute.hwclouds-dns.com

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
2
3
4
5
6
7
8
9
10
11
12
13
应用(例如:curl www.example.com)
|
v
libc resolver(/etc/resolv.conf 指定 nameserver) -> 检查 /etc/hosts(由 nsswitch.conf 指定顺序)
|
v
本地缓存/转发器(可选:systemd-resolved、dnsmasq)
|
v
递归解析器(ISP 或 公共 DNS,如 1.1.1.1/8.8.8.8)
|
v
根服务器 -> TLD 服务器 -> 权威域名服务器(最终返回 A/AAAA/PTR 等记录)

「Linux 网络编程」DNS 域名解析、域名与 IP 地址的转换
https://marisamagic.github.io/2025/10/10/20251010/
作者
MarisaMagic
发布于
2025年10月10日
许可协议