1.1 网络分层与数据包的逐层传输
程序说一句 GET / 很容易,网络栈却要回答一串具体问题:目标主机是谁,途中走哪条路,到达后交给哪个进程,某一段丢失又由谁补发?若把这些职责都压进应用代码,换一张网卡都可能牵动整个系统。
分层把职责分配给相邻接口。底层 Ethernet 可以换成 Wi-Fi,上层 HTTP/1.1 可以换成 HTTP/2,其他层不必随之重写;这正是分层给实现和排障带来的隔离边界。
现实协议不会严格经过七个彼此独立的软件模块:实现会跨层优化,中间盒也可能观察甚至修改多个层次。后文会用正式的 header、地址和状态来说明每一步。
1. 两套常见模型
OSI 七层模型适合用统一术语描述职责;互联网工程通常采用更紧凑的 TCP/IP 分层:
| TCP/IP 视角 | 对应 OSI 范围 | 常见协议或机制 |
|---|---|---|
| 应用层 | 应用、表示、会话 | HTTP、DNS、TLS、SSH |
| 传输层 | 传输 | TCP、UDP、QUIC 所用 UDP |
| 网络层 | 网络 | IPv4、IPv6、ICMP/ICMPv6、路由 |
| 链路层 | 数据链路 | Ethernet、Wi-Fi、ARP、VLAN |
| 物理介质 | 物理 | 铜缆、光纤、无线电 |
TLS 常被画在应用层与传输层之间;QUIC 又把加密、可靠传输和多路流组合在 UDP 之上。模型的价值是追问“这个功能由谁负责”,而不是争论协议应该塞进唯一格子。
这里把物理介质单列,便于说明传输条件;常见 TCP/IP 四层模型会把它并入链路层。NDP 虽负责邻居发现等本地链路功能,报文却通过 ICMPv6 承载,不能直接当成“IPv6 版 ARP 帧”。
2. 封装:每层增加自己的控制信息
浏览器交给 HTTP 的是一段请求语义。向下传递时,各层加入自己的 header,链路层还可能加入 trailer:
application bytes
└─ transport header + application bytes
└─ IP header + transport packet
└─ link header + IP packet + link trailer接收方反向解析,并依据 header 字段把数据交给正确上层,这叫 demultiplexing:
这个封装图只表示包含关系,不保证一条应用消息恰好对应一个 TCP segment 或一帧。TCP 会拆分或合并字节,TLS 记录也有自己的边界;HTTP/2 和 QUIC 的流还会在同一连接中复用。
- Ethernet type 可以指示 IPv4 或 IPv6;
- IP next-header/protocol 可以指示 TCP、UDP 或 ICMP;
- TCP/UDP destination port 帮助内核找到 socket;
- TLS SNI 可选择目标服务名,ALPN 协商应用协议;HTTP Host/:authority 标识请求目标的 authority。
这些字段处在不同信任与可见边界。启用 TLS 后,链路和网络设备仍能看到 IP/端口等外层信息,但不能正常读取加密的 HTTP 内容。QUIC 进一步加密了更多传输控制信息,却仍需要 UDP/IP 外层完成转发。
3. 一次 HTTPS 请求
下面假设客户端建立一个新 HTTPS 连接,无代理且名字已在本地测试环境配置。www.example.test 使用保留测试后缀,不能指望公共 DNS 直接解析。访问 https://www.example.test/catalog 的概念步骤是:
- 解析名字:浏览器或系统 resolver 查询 DNS,得到一个或多个 IPv4/IPv6 地址;
- 选择地址与路由:内核根据路由表选择下一跳和出口接口;
- 解析邻居:在 Ethernet 等需要链路地址的本地网络中,用 ARP(IPv4)或 NDP(IPv6)找到下一跳链路地址;
- 建立传输:TCP 完成握手,或客户端通过 UDP 启动 QUIC;
- 建立安全会话:TLS 验证服务身份并协商密钥;QUIC 内置 TLS 1.3 握手;
- 交换应用消息:发送 HTTP 请求,接收响应;
- 复用或关闭:连接可以服务后续请求,也可能按协议关闭。
缓存会跳过部分步骤:DNS 结果可能已缓存,连接池可能已有可用连接,TLS session 可以恢复。抓到的网络路径因此取决于当时状态。
这不是所有协议都顺序执行的七次网络往返。QUIC 的传输与 TLS 握手互相配合;恢复会话可能减少握手成本,不能笼统说跳过全部认证或密钥状态。名字解析本身也可能触发网络访问。
4. 地址、端口与名字回答不同问题
| 标识 | 作用范围 | 回答的问题 |
|---|---|---|
| 域名 | 应用/命名系统 | 想访问哪个逻辑服务? |
| IP 地址 | 网络层 | 包应路由到哪个网络接口或节点? |
| TCP/UDP 端口 | 传输层 | 到达主机后交给哪个 endpoint? |
| MAC 地址 | 单条二层链路 | 当前这一跳把帧交给谁? |
| socket 五元组 | 传输会话 | 哪条协议流? |
常见五元组是源 IP、源端口、目标 IP、目标端口、传输协议。NAT 或代理会改变其中部分字段,所以客户端和服务器观察到的五元组可能不同。
这是常见 TCP/UDP 流标识,不是所有连接身份的完整定义。QUIC 可以借助 connection ID 在地址改变后保持连接;应用层连接池和代理又有自己的状态范围。
域名也不等于一台固定服务器。DNS 可以按位置、健康或策略返回多个地址,反向代理/CDN 再根据 Host、SNI、路径或其他信息选择后端。
5. 每一跳会变什么
假设 Host A 通过两个路由器访问 Host B:
A ── link 1 ── R1 ── link 2 ── R2 ── link 3 ── B每经过一个路由器:
- 入站链路帧被接收并移除;
- 路由器检查 IP header,递减 IPv4 TTL 或 IPv6 Hop Limit;
- 查询路由,选择下一跳;
- 为下一条链路创建新的 frame;
- frame 的源/目标链路地址随这一跳变化。
端到端 IP 地址通常保持不变,但 NAT、隧道或代理可能改变或包裹它。TCP header 通常由端点解释,中间盒仍可能跟踪或修改部分字段。把“二层每跳变化、三层永远不变”写成绝对规则会漏掉现代网络现实。
例如主机为 192.0.2.10/24、网关为 192.0.2.1,且只有直连路由和默认路由。发送到本子网主机时解析对方的链路地址;发送到 203.0.113.20 时解析网关的链路地址,IP 目标仍是远端。下面只用 Python 3 做这个模型的判断,不读取或修改实际路由表:
from ipaddress import ip_interface, ip_address
interface = ip_interface("192.0.2.10/24")
gateway = ip_address("192.0.2.1")
for target in [ip_address("192.0.2.20"), ip_address("203.0.113.20")]:
next_hop = target if target in interface.network else gateway
print(target, next_hop)
# 192.0.2.20 192.0.2.20
# 203.0.113.20 192.0.2.1实际系统还要考虑更具体路由、策略路由和邻居缓存;跨网段并不意味着去 ARP 查询远端服务器的 MAC。若从 A 发出时 TTL 为 64,沿图中两次路由转发到达 B 时通常为 62;普通二层交换机不为这种转发递减 IP TTL。
6. 交换机、路由器与代理
交换机主要依据链路层地址在同一二层广播域内转发 frame,并学习源 MAC 对应端口。
路由器在不同 IP 网络之间转发 packet,依据最长前缀匹配选择下一跳,并减少 hop limit。
四层负载均衡器通常按 IP、port 和 transport flow 分发连接。
七层反向代理理解 HTTP 等应用协议,可以按域名、路径、header 或身份路由。
一个设备可以同时承担多种角色。云网络的“路由器”也可能由分布式软件实现,抓包时未必能看到传统物理设备上的全部中间过程。
7. MTU 与头部开销
链路一次可承载的网络层 packet 大小受 MTU 限制。若 Ethernet MTU 为 1500 字节,一个 IPv4/TCP packet 的应用 payload 通常小于 1500,因为还要容纳 IP 与 TCP header;TCP option、IPv6、隧道和加密又会改变可用空间。
不要把“1500 - 20 - 20 = 1460”当作任何连接的永久 MSS:
还要区分 SYN 中通告的 MSS 与某个 segment 实际能放下的数据。按 RFC 9293 §3.7.1,基于 MTU 计算通告 MSS 时减固定 IP/TCP 头长度;实际发送时,再为使用的 IP/TCP option 减少数据长度。TLS 开销占用 TCP 数据空间,不直接改写 MSS 字段。
- IPv4 header 可以含 option;
- TCP header 可以含 option;
- IPv6 基础 header 大小不同,还可能有 extension header;
- VPN/overlay 在外面再包一层;
- path 上最小 MTU 可能低于出口接口 MTU。
IPv4 路由器在特定条件下可以分片;IPv6 路由器不做这种中途分片,发送端需要依据 Path MTU 调整。可靠协议通常尽量避免 IP fragmentation。
例如路径 MTU 为 1500、IPv4 无 option、TCP 使用 12 字节 option 时,在不分片且对端 MSS 允许的条件下,该段最多携带 1500-20-32=1448 字节 TCP 数据。通告值仍可能是 1460;其中属于 HTTP 的字节还要扣除 TLS 等应用封装。
8. Socket API 暴露的是哪一层
常用 socket 类型:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as tcp:
print(tcp.family == socket.AF_INET, tcp.type == socket.SOCK_STREAM)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp:
print(udp.family == socket.AF_INET, udp.type == socket.SOCK_DGRAM)
# True True
# True TrueSOCK_STREAM 提供字节流接口,应用看不到 TCP segment 边界;SOCK_DGRAM 保留 datagram 边界,但仍要处理丢失、乱序、重复和大小限制。
本例明确选 AF_INET,对应 IPv4 TCP/UDP;这两个 socket 只创建并自动关闭,没有绑定端口、建立连接或发送数据。UDP 接收缓冲若小于单个 datagram,某些接口会截断并丢弃剩余部分,不能像 TCP 那样靠下一次读取补回同一消息。
普通应用通常不直接构造 IP/Ethernet header。raw socket、packet socket 或抓包接口会暴露更低层能力,也需要平台权限和不同的校验和/路由处理规则。下一章会把 socket 的字节流语义讲透。
9. localhost 也遵守协议语义
访问 127.0.0.1 或 ::1 时,packet 由 loopback interface 处理,通常不会经过物理网卡。TCP 仍提供连接、字节流、序号和关闭语义,socket 程序也仍会遇到短读、短写和消息边界问题。
因此,本机测试能验证应用 framing 和状态机,却不能代表真实网络的 RTT、丢包、MTU、中间盒与带宽。需要使用 network namespace、traffic control、虚拟机或隔离测试网络引入这些条件。
10. 分层排障不是从第一层机械走到第七层
遇到“网站打不开”,先收集能快速缩小范围的证据:
| 现象 | 下一步证据 |
|---|---|
| 域名解析失败 | resolver 配置、DNS response、record 与 TTL |
| 有地址但连接超时 | 路由、抓包中的 SYN、ACL/防火墙、回程路径 |
| 立即 connection refused | 目标到达但端口无 listener,或设备主动拒绝 |
| TCP 成功但 TLS 失败 | SNI、证书链、时间、版本/密码套件、ALPN |
| TLS 成功但 HTTP 错误 | status、header、proxy/backend log、request ID |
| 只有大请求失败 | Path MTU、fragmentation、buffer 与 timeout |
| 只有高并发失败 | backlog、连接池、端口/FD、排队与资源上限 |
ping 只测试某类 ICMP 可达性。ping 不通可能是 ICMP 被过滤,ping 通也不能证明 TCP 443、TLS 和应用都健康。
分层的正确用法是把证据映射到职责,再验证相邻层接口,而不是靠一条命令宣布“网络没问题”。
11. 常见误解
“OSI 有七层,所以每层都有独立 header。” 表示/会话层常以库和应用协议形态存在,并非每个 packet 都有七个可分离 header。
“TCP 负责找到目标主机。” IP 和路由处理跨网络到达;TCP 负责端到端可靠字节流与拥塞控制等。
“端口属于某个进程。” 端口是 transport endpoint 标识的一部分,内核按地址、端口、协议和 socket 状态做 demultiplex;多个 socket 可在规则允许时共享端口。
“HTTPS 是 HTTP 加一个加密 header。” HTTP/1.1 或 HTTP/2 经 TLS/TCP 传输时使用 TLS 记录与密钥状态;HTTP/3 则由 QUIC 使用 TLS 握手导出的密钥保护数据包,不使用 TLS 记录层。
“抓到一个 frame 就能看到端到端所有跳。” 普通抓包点只看本接口收发的链路表示;其他 hop 会重新封装。
12. 小结
分层模型把网络职责拆成可替换的接口:
- 应用层定义消息语义;
- transport 连接 endpoint,并提供字节流或 datagram;
- IP 让 packet 跨网络逐跳转发;
- link layer 负责当前一跳的 frame;
- DNS、TLS、proxy 和 NAT 让真实请求路径比四层表更丰富;
- 封装增加 header,demultiplexing 依据 header 找到上层;
- 排障要从症状收集证据,再定位到具体层和状态。
分层模型已经解释请求怎样跨越链路。下一章进入2.1 TCP Socket 的生命周期,从 connect/accept 开始处理短读、EOF 与半关闭。