在你敲下 curl https://example.com 到屏幕上跳出返回内容之间,发生了一长串你大概从没见过的事。这一章我们顺着这个请求走完整条路——看清它穿过的每一层,顺便回答一个根本问题:网络为什么非要搞得这么多层?
网络分层(Network Layering):把"让两台电脑通信"这个庞大问题,切成若干层,每层只解决一个子问题、只跟相邻的上下层打交道。
1这个请求到底经历了什么
curl 在用户空间(user space)拼好一段 HTTP 文本,大致是 GET / HTTP/1.1 加上 Host: example.com。但你的网卡根本不认识 HTTP,它只会收发一串带电信号的字节。从这段文本,到网卡真正发出去的信号,中间隔着四层加工。每往下一层,就在数据外面包一层信封——这个过程叫封装(Encapsulation):
- 应用层(Application):curl 得先知道 example.com 的 IP。它调用
getaddrinfo()触发 DNS 查询(这条链 0.5 细讲),拿到比如93.184.216.34。 - 传输层(Transport):curl 调
connect(),内核发起 TCP 三次握手(0.4 细讲)建好连接。HTTP 文本被塞进一个 TCP 段,前面贴上 TCP header——记录源端口、目的端口(443)、序列号。 - 网络层(Internet):TCP 段再被塞进一个 IP 包,贴上 IP header——记录源 IP、目的 IP、TTL。内核查路由表(0.3 细讲),发现目标不在本地网段,决定把包先交给默认网关,也就是你家路由器。
- 链路层(Link):要把包发给路由器,得知道路由器的 MAC 地址。内核用 ARP 喊一句"谁是 192.168.1.1"(0.2 细讲),拿到 MAC,贴上 以太网 header,封成一个完整的以太网帧。
- 网卡(NIC)把这个帧变成电信号或无线电波,发出去。
层层贴 header 的过程,跟 Express 的 middleware 一模一样:一个 req 进来,每层 middleware 包一层逻辑再往下传。或者想成套娃——HTTP 数据在最里层,外面依次套上 TCP、IP、以太网三层信封,每层设备只拆开、只读自己关心的那一层。
2经过路由器时发生了什么
帧抵达你家路由器,而路由器做的事,正是"分层处理"的一个缩影:
- 拆开链路层信封(以太网 header),确认这帧确实是发给自己的;
- 读网络层信封(IP header),发现目的 IP 在外网、不属于本地网段 → 需要转发;
- 做 SNAT(0.6 细讲):把源 IP 从你的私网地址
192.168.1.x改成路由器自己的公网 IP,并记下这条映射关系,这样回包才找得回你; - 重新贴一个新的链路层信封(这次下一跳是 ISP 的设备),从 WAN 口发出去。
这个包接着在 ISP 网络里被一台台路由器接力转发,每一跳都重复"拆链路层 → 看 IP → 重贴链路层",直到抵达 example.com 的服务器。服务器收到后做的是完全相反的操作——解封装(Decapsulation):剥掉以太网 header → 剥掉 IP header → 剥掉 TCP header → 把最里层的 HTTP 请求交给 nginx。响应再沿原路、按同一套封装流程返回。
上面这第 2 节,就是一台路由器存在的全部意义。等你读 OpenWRT 源码时会发现,它恰好按"层"把活儿拆给了不同组件:netifd 管接口和链路层,firewall4 / nftables 管 IP 层的转发、过滤和 NAT,dnsmasq 管应用层的 DNS 和 DHCP。整个 OpenWRT,就是这张分层图的一份具体实现——你现在脑子里画的这张图,Phase 2 会被一块一块拆开来看。
3那么,为什么要分层
设想一下完全不分层会怎样:curl 的代码得自己处理 Wi-Fi 信号调制、MAC 寻址、IP 选路、TCP 丢包重传,外加 HTTP 语义——全揉进一个函数。换块网卡、换种链路(Wi-Fi 换成有线),整套代码推倒重写。分层换来了两样东西:
- 关注点分离(Separation of Concerns):每层有清晰接口,对上提供服务、向下索取服务,层内的实现细节互不可见。TCP 不关心底下是 Wi-Fi 还是光纤,IP 不关心上面跑的是 HTTP 还是 SSH。
- 可替换性:把 Wi-Fi 换成以太网,上面的 TCP / HTTP 一行不用改;把 HTTP 换成 SSH,下面的 IP / 以太网一行不用改。
你写 TS 从不操心底层跑的是 ARM 还是 x86 指令——编译器和运行时帮你把底层分层抽象掉了。网络只是把这种"分层抽象"做到了硬件和协议的层面而已。
OSI 七层是教科书里的理论模型,TCP/IP 四层才是实践中真正在用的。本课程站 TCP/IP 四层,但会偶尔借用 OSI 的层号黑话——比如"二层交换机"(工作在链路层)、"三层路由"(工作在网络层)。听到别人说"几层",通常指的是 OSI 编号。
4实验:亲眼看见分层
理论讲完,抓一次包就全明白了。开两个终端:
# 终端 A:开始抓包(需要 sudo)。Linux 用 any 抓所有接口
sudo tcpdump -i any -n host example.com -v
# 终端 B:发一个请求
curl https://example.com
回到终端 A,你会先看到 TCP 三次握手(Flags [S] → [S.] → [.]),接着是 TLS 握手,然后是加密的应用数据。tcpdump 每打印一行,内容正是某一层 header 里的字段。
macOS 上没有 any 伪接口,把 -i any 换成你的实际接口,通常是 -i en0(Wi-Fi)。先跑 route get default 或 ifconfig 确认接口名。
想看得更直观,用 Wireshark 抓同一个请求,随便点开一个包,你会看到一棵可以展开的树:
- Frame → 抓包的元信息
- Ethernet II → 链路层,源 / 目的 MAC
- Internet Protocol → 网络层,源 / 目的 IP
- Transmission Control Protocol → 传输层,端口和序列号
- 最里面 → 应用层数据(TLS 加密后看不到明文)
这棵树,就是"封装"的可视化——你亲眼看到一个 HTTP 请求被三层信封一层层包着。
跑 curl -v https://example.com,它会按 DNS → TCP → TLS → HTTP 的顺序,把每一步都打印出来。对照 tcpdump 抓到的包一起看,分层一目了然。
本章小结
- 一个 curl 请求自上而下穿过应用层、传输层、网络层、链路层,每层贴一个自己的 header——这叫封装;到对端再逐层剥开——这叫解封装。
- 分层换来两件事:关注点分离 与 可替换性。
- 实践中用 TCP/IP 四层模型,口头上常借 OSI 的层号说话。
- 路由器是这套模型在链路层 + 网络层的实现;OpenWRT 把每一层分给了不同组件(netifd / nftables / dnsmasq)。
动手练习
- 用 tcpdump 抓一次
curl https://example.com,数数 TCP 握手一共用了几个包。 - 在 Wireshark 里点开一个包,把它的分层树状结构截图保存——这张图后面几乎每一章都会用到。
- 思考题:
ping example.com(ICMP)在 Wireshark 里为什么看不到 TCP 那一层?(提示:ICMP 属于哪一层?) - 进阶:跑
curl -v,把它打印的分层步骤,和 tcpdump 抓到的包一一对应起来。