引言

敲下回车键的那一刻,屏幕上加载图标开始转动。2.3 秒后,页面完整呈现。这 2.3 秒里,网络协议栈在最底层完成了数十次往返,十几层协议交替工作,最终把一段 HTML 文字还原到屏幕。

大多数文章会从"OSI 七层模型"开始讲起,依次介绍每层的职责。读完之后确实记住了七层分别叫什么名字,但"敲下回车到页面出现"这个过程在脑子里仍然是一团浆糊——因为这些文章始终没有把七层和实际发生的事对应起来。

这篇文章换一个思路:先看真实抓包数据,再反过来解释每层在干什么。三种不同场景的对比做完之后,七层模型的真正价值才显现出来——它不是用来背的,是用来定位问题的。


一、先看一条真实抓包

在浏览器里访问 https://example.com,Wireshark 捕获到的第一条数据包大致如下(简化结构):

Frame 1: 74 bytes on wire, 74 bytes captured
    Ethernet II, Src: HuaweiTe_xx:xx:xx, Dst: IntelCor_xx:xx:xx
    Internet Protocol Version 4, Src: 10.0.0.187, Dst: 93.184.216.34
    Transport Layer Security
    TLS Record Layer: Application Data Protocol: http-over-tls

四个字段分别对应四层:链路层(Ethernet)、网络层(IPv4)、传输层(TLS)、应用层(HTTP)。没有"会话层",没有"表示层"——这两层在实际的 TCP/IP 协议栈里不是独立存在的,或者说,它们的功能被分散塞进了 TLS 层和应用程序本身。

这个对应关系搞清楚之后,后面的内容才不容易混淆。


二、协议栈四层:每层的职责边界

先用一张图说清楚每层做什么:

物理层(Layer 1)

电信号/光信号
网卡/光纤/双绞线

链路层(Layer 2)

Ethernet · 局域网转发
ARP · IP→MAC 映射

网络层(Layer 3)

IP · 路由与寻址
ICMP · 差错通知

传输层(Layer 4)

TCP · 可靠传输
UDP · 无连接

应用层(Layer 7)

HTTP/HTTPS · DNS · SSH · SMTP

每层关注点完全不同:

链路层解决"相邻节点之间怎么传帧"。MAC 地址是这层的身份证,以太网帧在同一个局域网内被交换机转发。出了局域网的大门,这层就结束了——下一跳的 MAC 地址会被替换成新的。

网络层解决"数据包怎么从 A 点到达 B 点"。IP 地址是这层的身份证,路由器根据 IP 路由表把包从源网络送到目标网络,跨网段的访问全部依赖这层。NAT(网络地址转换)也发生在这层。

传输层解决"给哪个进程或服务"。端口号是这层的身份证。IP 找到机器之后,端口号决定数据交给哪个监听进程。TCP 保证有序和可靠,UDP 什么都不保证但延迟更低。

应用层解决"传的是什么"。HTTP 定义了请求和响应的语义,TLS 在这层之上加密整个对话,DNS 把域名翻译成 IP。

理解了这四层的职责边界,再来看三个不同的访问场景,会发现同一个 HTTPS 请求在不同拓扑下的路径差异完全可以用这几层的组合来解释。


三、封包结构:每向右一步,多一层头

“每向右一步,数据包就被多封装一层”——这句话是理解网络协议的关键。用一张图展示一个 HTTP GET 请求从生成到发送,完整经过的封装过程:

物理层

链路层

网络层

传输层

应用层

HTTP 请求头
GET /index.html HTTP/1.1

TLS Record
加密内容 + 序列号

TCP Segment
源端口 52431 · 目标端口 443
Seq · Ack · Flags · Window

IP Packet
源 IP 10.0.0.187 · 目标 IP 93.184.216.34

Ethernet Frame
源 MAC · 目标 MAC · Type: 0x0800

01010111...
电信号/光信号在物理介质上传输

每一层都会在原始数据前面加上一段"头部(Header)",里面写着这层需要的所有控制信息。响应返回时,这个过程完全倒过来——每经过一层,对应的头部被剥掉,露出里面的内容。

MTU 与分片是这里一个重要的细节。以太网的默认 MTU(Maximum Transmission Unit)是 1500 字节。如果一个 IP 包的大小超过 1500 字节,网络层会在发送前把它拆成多个小包(分片),到达目标后再重新组装。这个过程发生在 IP 层,对上层是透明的——TCP 和应用层完全不知道分片发生过。但如果中间某个分片丢失,整个 IP 包就要全部重传,这是高延迟网络下的性能杀手。


四、三种场景:同样的 HTTPS,不一样的路径

场景一:Kubernetes Pod 访问云数据库

代码里这样写:

import pymysql
conn = pymysql.connect(host='rm-uf6z9g5z1x9y.mysql.tencentcdb.com',
                       port=3306,
                       database='orders')

Pod 和 RDS 在同一个私有网络 VPC 内,这条请求的网络路径如下:

同一 VPC 私有网络

veth → eth0
无 NAT

弹性网卡
直接路由

L2 转发

Pod A
10.1.3.45

Node 主机
10.1.1.20

云 VPC 交换机
二层转发

RDS MySQL
10.1.8.120

数据包从 Pod 的 veth 设备出网卡,进入 VPC 交换机(云内二层),直接路由到 RDS 的弹性网卡。整个过程只经过三层:Pod 容器网络接口 → Node 主机网卡 → 云 VPC 交换机 → RDS 网卡。没有经过任何公网设备,延迟在 1ms 以内。

这里有一个 Docker 系列留的伏笔:Pod 访问集群外部服务时,源 IP 会经过 SNAT(Source NAT,源地址转换)变成 Node 的节点 IP。代码里看到的连接来源 IP 并不是 Pod 的 IP,而是节点的 IP——这个机制在容器网络那篇文章里已经展开过了。

场景二:跨地域用户访问 CDN

用户在上海,访问一个静态资源 https://static.example.com/bundle.js

HTTPS 443
公网出口

DNS 智能解析
返回最近节点 IP

未命中缓存
回源请求

命中缓存
直接返回

浏览器
上海用户

上海电信骨干网

CDN 节点
杭州 120.25.x.x

源站
北京 10.10.x.x

用户的请求并不直接打到源站,而是先打到 DNS 智能解析返回的最近 CDN 节点。CDN 节点如果缓存了内容,直接返回(命中率决定了这个环节的响应时间);如果没有缓存,CDN 节点代替用户向源站请求,这个动作叫回源

背后是 Anycast 机制:北京源站对外广播同一个 IP,运营商骨干网根据 BGP 路由把各地区请求分发到最近的物理节点。用户感觉不到中间层存在,只觉得"加载很快"。

HTTPS 握手在这里多了一个环节——TLS 终止代理(TLS termination)。用户和 CDN 节点之间建立一条加密通道,CDN 节点和源站之间建立另一条加密通道。CDN 节点持有源站的证书密钥,代替源站和用户完成 TLS 握手。源站的真实 IP 在这个场景下对用户完全不可见,这是 CDN 安全防护的第一层。

场景三:跨运营商访问:移动用户请求到达电信机房的全过程

用户用手机通过 4G 网络访问 https://api.example.com,手机接入中国移动,API 服务部署在阿里云华东节点(电信网络)。这两个网络之间的跨运营商通信是理解网络延迟和 DNS 解析绕路的关键场景:

无线空口
基站接入

跨运营商出口
对等互联带宽

进入电信网络

内网低延迟

VPC 内网

手机终端
中国移动 4G

移动骨干网
CMNET

移动/电信
对等互联点

电信骨干网
ChinaNet

API 网关
鉴权/JWT

业务服务
VPC 内网

跨运营商访问的第一个性能瓶颈在对等互联点(IXP)。国内三大运营商(移动、电信、联通)之间的流量通过有限的对等互联带宽交换,当某条互联链路拥塞时,跨运营商请求的延迟会突然跳升——同一个服务,电信用户访问 15ms,移动用户可能要 45ms,差距就是从这里来的。

DNS 解析在这个场景下有第二个坑:手机通常使用运营商分配的本地 DNS 服务器(递归解析器),DNS 查询经过移动 DNS → 根 DNS → TLD → 权威 NS 多个跳,如果权威 NS 返回的 IP 不在用户所属网络的同一运营商范围内,请求会额外多走一段跨运营商路径。DNS 智能解析(根据请求来源 IP 返回最优 IP)和 **HTTP DNS(HTTPDNS)**就是解决这个问题的方案——绕过运营商 Local DNS,根据客户端实际 IP 直接返回最优接入点。

TLS 1.3 的 1-RTT 握手在跨运营商高延迟场景下是性能瓶颈——移动网络空口本身已经有 30~100ms 的 RTT,加上跨运营商骨干网的额外延迟,一次 HTTPS 握手可能消耗 200ms 以上。HTTP/3 的 QUIC 协议在 UDP 之上运行,连接复用和 0-RTT 模式可以显著改善这个场景下的握手开销。这是后续文章会展开的协议演进逻辑,这里先埋个伏笔。


五、一次 HTTPS 请求的完整时序

把三种场景里的共同路径抽出来,看看每层在一次 HTTPS GET 请求里的完整动作:

目标服务器 交换机/路由器 HTTP 层 TLS 层 TCP 层 Linux 内核协议栈 网卡驱动 浏览器 目标服务器 交换机/路由器 HTTP 层 TLS 层 TCP 层 Linux 内核协议栈 网卡驱动 浏览器 应用层生成请求 生成 HTTP GET /index.html HTTP 报文传入 TLS 层加密 TLS 密文传入 TCP 层分片 TCP 段传入 IP 层封装 IP 包传入网卡驱动 电信号在物理层传输 交换机/路由器按路由表转发 IP 包到达目标,TCP 重组 TCP 递送密文 TLS 解密 HTTP 报文交付应用进程 响应沿原路返回

六、为什么说"七层是给人类看的"

回到 OSI 七层模型和实际协议栈四层的对比。很多人学完七层之后还是搞不清"表示层"和"会话层"到底对应哪个协议、哪段代码。原因很简单:这两个层在 TCP/IP 体系里不是独立存在的。

**TLS 同时承担了表示层和会话层的职责:**加密(表示层的 Confidentiality)由 TLS 的加密算法实现,会话复用(会话层的 Session resumption)由 TLS Session ID 或 Session Ticket 实现。HTTP/2 的多路复用把多个流塞进同一条连接里,本质上也是会话层的逻辑。这两个功能在 OSI 设计者的年代根本不存在,所以七层模型只能强行把它们塞进"应用层"顶部的灰色地带。

理解了这个背景之后,再看七层模型就清晰了——它是一个教学工具,用来帮助人类讨论"这层出了问题该找谁负责":

网络不通
ping 不通

连接建立失败
握手超时

请求内容错误
503/502

查交换机/路由器
IP 配置/路由表

查端口占用
防火墙规则/队列长度

查应用日志
服务进程状态

故障现象

哪层问题?

2-3 层
链路层/网络层

4 层
传输层

7 层
应用层

逐跳排查

socket 状态分析

应用层诊断

每次排查网络问题,其实都是在脑子里过一遍这个模型。哪层出问题就从哪层入手,不要在错误的层面浪费排查时间。


七、知识框架

网络协议
全貌

链路层

MAC 地址 · Ethernet 帧结构

交换机转发 · CAM 表

ARP 协议 · IP→MAC 映射

VLAN · 链路聚合

网络层

IP 地址与路由

ICMP 差错报告 · traceroute

NAT 与地址转换

VPC 私有网络

分片与重组 · MTU

传输层

TCP 可靠传输机制

UDP 无连接特性

端口号 · socket 对

NAT 穿透 · hole punching

应用层

HTTP/HTTPS 语义

DNS 域名解析

TLS 加密与认证

QUIC/HTTP/3 演进

场景对比

Pod 访问云数据库 · VPC 内网低延迟

CDN 就近节点 · Anycast + 回源

跨运营商访问 · 移动→电信对等互联延迟


总结

这篇文章的核心只有一件事:把"敲下回车到页面出现"这个过程拆开,看看每层到底做了什么。

链路层解决相邻节点的帧传递,网络层解决跨网段的路由寻址,传输层解决进程级的端口分发,应用层解决请求本身的语义。跨运营商访问场景下,网络层的路由选择和 DNS 解析共同决定了请求的最终路径。OSI 七层模型的真正价值不在于背诵,在于定位:哪层出问题就该从哪层入手。

三种场景对比完之后,下一篇文章聚焦 TCP 三次握手——为什么有时候连接建立到一半就卡住了,全连接队列和半连接队列是怎么配合工作的,Linux 里有哪些指标可以提前发现连接建立的瓶颈。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐