一次网页打开,就是七层各干一次活、各加一个头。 中间设备只处理到自己那一层为止——这是分层最实际的价值。
1.5 讲清了分层是什么,1.6 用一次完整的通信把七层走一遍。 1.6.2 与 1.6.3 会分别放大上半段与下半段,这一节先看全景。
一、一次网页打开的全程
在浏览器里敲一个地址,七层各自做了什么:
| 层 | 动作 | 产物 |
|---|---|---|
| 应用层 | 生成 HTTP 请求 | GET / HTTP/1.1 |
| 表示层 | 定编码、压缩 | Accept-Encoding: gzip |
| 会话层 | 管连接与身份 | Cookie、keep-alive |
| 传输层 | 分段、加端口 | TCP 段,20 字节头 |
| 网络层 | 定路线、加 IP | IP 包,20 字节头 |
| 链路层 | 找下一跳地址 | 以太网帧,14 字节头 |
| 物理层 | 变成信号 | 电/光 |
最上面三行在 TCP/IP 里是同一层干的: HTTP 一个协议就把「要什么」「能不能压缩」「带哪个 Cookie」全写进头部了。 所以真实抓包时看不到「表示层」和「会话层」的独立头部——它们变成了应用层头部里的几个字段。 第四行的分段是这一节的转折点:从这一层开始,数据要被「切」, 因为底下有长度上限(MTU)。 切法不同,就导向了 1.6.3 讲的两种命运(TCP 的 1460 与 UDP 的 1472)。
二、发送端加头,接收端剥头
两端是完全镜像的两套动作:
| 动作 | 发送端 | 接收端 |
|---|---|---|
| 顺序 | 从上往下加头 | 从下往上剥头 |
| 每层 | 在前面插入本层头 | 看本层头、再剥掉 |
| 检查 | 算校验和填进去 | 重算,不对就丢 |
| 最终 | 交给网卡发出去 | 交给应用程序 |
第三行那个「不对就丢」是很多诡异的根源:校验和错了,接收端不会通知你,直接丢掉。 发送端只知道「没收到确认」,不知道「为什么没收到」。 于是只能重传——这就是为什么网络问题常常表现为「慢」而不是「错」。 第四行那个「交给应用程序」是分层的终点:应用程序看到的永远是干净的原始数据, 所有头部都已经被剥掉,一个字节都没有多。 这也是为什么写应用的人可以不关心网络细节。
三、中间设备只到某一层
这是分层最实际的收益,也是最容易忽略的一点:
| 设备 | 处理到第几层 | 看不看得到内容 |
|---|---|---|
| 中继器、集线器 | 物理层 | 看不到,只放大信号 |
| 交换机 | 链路层 | 看 MAC,不看 IP |
| 路由器 | 网络层 | 看 IP,不看端口 |
| 防火墙、代理 | 传输层以上 | 看得到端口与内容 |
这四行解释了一个常见困惑:为什么换一个交换机,IP 地址不用改? 因为它只处理到链路层,根本不知道 IP 的存在。 而换一个网段就必须改 IP,因为路由器是按 IP 转发的。 最后一行是「分层」被打破的地方: 防火墙要看端口号,就必须解析到传输层;要看内容(比如拦某个关键词),就得解析到应用层。 解析得越深,性能越差、也越容易被加密挡住——这就是为什么加密普及之后,深度检测越来越难做。
四、动手 Demo:七层通信,一层一层加头
从 27 字节的 HTTP 报文出发,一层一层加头,看累计长度。 这就是本节第一张表的代码版。
import _netsim as N
app = b'GET / HTTP/1.0\r\n\r\n'.ljust(27, b'x')
print('%-8s %-16s %-8s %s' % ('层', '动作', '本层头', '累计'))
print('-' * 48)
print('%-8s %-16s %-8s %d' % ('应用层', 'HTTP 报文', '—', len(app)))
seg = N.tcp4h(40000, 80, flags=0x18) + app
print('%-8s %-16s %-8s %d' % ('传输层', '加 TCP 头', N.TCP_HDR, len(seg)))
pkt = N.ip4h('192.168.3.152', '93.184.216.34', seg)
print('%-8s %-16s %-8s %d' % ('网络层', '加 IP 头', N.IP_HDR, len(pkt)))
frm = N.eth('00:11:22:33:44:55', '9c:b6:d0:0d:d5:0f', pkt)
print('%-8s %-16s %-8s %d' % ('链路层', '加以太网头', N.ETH_HDR, len(frm)))
print('物理层 发 %d 个比特' % (len(frm) * 8))
实跑输出:
--------------------------------------------------------------------
七层通信,一层一层加头
--------------------------------------------------------------------
**「7 层通信」在书上是张图;在代码里就是「把上一层的整包当成数据」。**
层 动作 本层头 累计长度
------------------------------------------------
应用层 HTTP 报文 — 27
传输层 加 20 字节头 20 47
网络层 加 20 字节头 20 67
链路层 加 14 字节头 14 81
**这张表里头部是空字节**(全 0),
**因为这里只关心长度,不关心内容**——**真实头部每个字段都有含义,4.7 与 6.7 会逐个拆。**
**「7 层」在 TCP/IP 里只体现为 4 次加头**,**但书上的七层图仍然有用**:
**它把「编码」「会话」这两件事单独标出来了**,**提醒你这两件事不能忘——哪怕协议栈不替你管。**
27 → 47 → 67 → 81,四层一共加了 54 字节头:头比数据本身还多一倍。 这就是小请求在线上很「贵」的直接原因。 头部这里填的是空字节(全 0),只关心长度:真实头部每个字段都有含义, 第四章会拆 IP 头,第六章会拆 TCP 头。 「七层」在 TCP/IP 里只体现为四次加头:因为最上面三层被并成一层了。 但书上的七层图仍然有用:它把「编码」和「会话」这两件事单独标了出来, 提醒你这两件事不能忘——哪怕协议栈不替你管。
30 秒实验
- 把
app改成 1400 字节,看链路层总长会不会超过 1514(以太网帧的实际上限)。 - 算一下:如果一次要传 10 KB 数据,TCP 至少要切成几个段?
- 想一下:为什么防火墙越「智能」越慢?用分层解释。
常见误区
- 以为七层在抓包里能看到七个头。 TCP/IP 里只有四个头,上面三层被并成了一层。
- 以为中间设备都懂全部七层。 交换机只到二层、路由器只到三层,正因为如此, 换交换机不用改 IP。
- 以为校验和出错会报错。 接收端直接丢包,不通知, 发送端只能靠超时重传——所以网络问题常表现为「慢」。
自测三问(点开对答案)
1. 一次网页打开,七层各自产生了什么? —— **应用层产生 HTTP 请求,传输层分段加端口,网络层定路线加 IP,链路层加 MAC 帧头,** **物理层变信号。** 上面三层的活都写进了 HTTP 头部。 2. 为什么换一个交换机不用改 IP,换一个网段就要改? —— **交换机只处理到链路层,看 MAC 不看 IP;路由器按 IP 转发**,网段变了 IP 就得跟着变。 3. 校验和错了为什么不直接报错给发送端? —— **因为接收端只是丢掉这个包**,没有回任何消息。发送端只能靠「没收到确认」推断,再重传。下一节放大上半段:会话层以上到底在干什么。
- ☐ (本节完,下一节 1.6.2 会话层以上的处理)