📘 第一章 了解Web及网络基础
📄 一句话概括

发送是逐层加头,接收是逐层剥头。加了几次就要剥几次,层数一致字节数才对得上。

📝 本节和前面几节的关系

上一节讲层的规矩,这一节讲字节在层之间怎么走。 这是全书唯一一次把「发送端 → 线上 → 接收端」完整走一遍的地方。

一、发送:每层只关心自己那一段

应用层把请求交给 TCP,TCP 加上 20 字节头(源端口、目的端口、序号、确认号、窗口、校验和); 交给 IP,IP 再加上 20 字节头(源地址、目的地址、TTL、协议号);交给链路层,再加上以太网头。 每一层看到的都只是「上层给的这段字节 + 我自己的头」,它不需要理解上层内容。 这就是封装(encapsulation)。 反过来说,每层的头都是给自己这一层的对端看的:TCP 头给对端 TCP 看,IP 头给路径上的路由器看, 以太网头给相邻的交换机看。

二、接收:逐层剥,逐层校验

Demo 里服务端从 127.0.0.1:55813 收到 64 字节, 内核已经替我们剥掉了以太网头(14 字节)和 IP 头(20 字节)、TCP 头(20 字节)。 所以应用层拿到手的就是干干净净的 64 字节 GET /index.html HTTP/1.1…。 注意:剥的过程在应用里看不见,因为它们全在内核里完成。 每剥一层都要做一次校验:以太网有 FCS,IP 有头校验和,TCP 有校验和。 校验不过就丢弃——丢弃是静默的,上层只能靠超时发现。

三、这条链上的三个数量关系

记住三个数,后面各章都用得上: | 关系 | 数值 | 用在哪 | |---|---|---| | MTU 减去 IP 与 TCP 头 | 1500−20−20=1460 | MSS 上限,TCP 分段依据 | | 最小以太网帧 | 64 字节(含 18 字节头尾) | 小于它要填充,抓包会看到 60 | | 环路最大 TTL | 初始 64 或 128 | 每跳减一,减到 0 就回 ICMP 超时 | 这三条是「能算」的那一类,不依赖任何设备,可以直接验算。 真实环境里还有 PPPoE(−8)、VLAN 标签(−4)、隧道(−20 起)继续吃 MTU,这也是「本地通、 跨网不通」最常见的原因。

四、动手 Demo:抓一段真实收发,逐层拆字节

让服务端把收到的原始字节原样交出来,再按层数一遍:哪些是内核剥掉的,哪些是应用能看见的。

H.SINK.raw.clear()
with H.server() as port:
    raw = H.send(port, REQ11)
got = H.SINK.raw[0]
print(u'收到 %d 字节' % len(got))
H.show_bytes(got, 96)      # 文本 + 十六进制两行

实跑输出:

--------------------------------------------------------------------
一次收发,逐层拆开看字节落在哪
--------------------------------------------------------------------
  服务端从 127.0.0.1:56064 收到 64 字节
  文本 GET /index.html HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n
  十六 47 45 54 20 2f 69 6e 64 65 78 2e 68 74 6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 31 32 37 2e 30 2e 30 2e 31 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 63 6c 6f 73 65 0d 0a 0d 0a

  第 1 步 链路层剥掉         14 字节以太网头(这里由内核完成,看不到)
  第 2 步 网络层剥掉         20 字节 IP 头,留下 TCP 段
  第 3 步 传输层剥掉         20 字节 TCP 头,留下应用数据
  第 4 步 应用层             64 字节 HTTP 报文
  回来的状态行               HTTP/1.1 200 OK
  回来的首部行数             3 行
  回来的正文                 40 字节
  接收是发送的反向操作:每层只剥自己那一段头,剥完交给上一层。
  发送端加了几次头,接收端就要剥几次——层数一样,字节数才对得上。

第一行十六进制 47 45 54 20 就是 GET 的 ASCII。 0d 0a 是 CRLF,两个字节一组的分隔符——收尾那个空行让它变成 0d 0a 0d 0a。 第 1 步 链路层剥掉 14 字节以太网头(这里由内核完成,看不到)——这句话是本节的要点: 分层是真的,但你看不见它发生。 回来的 40 字节正文就是 <html><body><h1>hello</h1></body></html>,首部只有 3 行。 一个能跑的最小响应,就这么点东西。

30 秒实验

  1. 在请求里加一行 X-Test: 1,重跑,看「应用层」那行的字节数变多少。
  2. 把 H.show_bytes 的 limit 改成 300,看能不能把整个请求打成十六进制。
  3. 算一算:如果 MTU 是 1400(PPPoE),MSS 变成多少。

常见误区

  1. 「应用能收到 TCP 头和 IP 头」——普通 socket 看不到,它们在内核里已经剥掉了,要用原始套接字才行。
  2. 「校验不过会重传」——以太网和 IP 的校验失败直接丢,重传是 TCP 的事,而且靠超时触发。
  3. 「MTU 1500 就能发 1500 字节数据」——要减掉 40 字节的 IP 与 TCP 头。
自测三问(点开对答案) 1. 发送时加了几次头?接收时剥几次? —— 各层一次,数目相等。链路层、网络层、传输层各一次,应用层不加头。 2. 为什么抓包看到的最小以太网帧是 60 字节,标准却说 64? —— 64 含 4 字节帧校验 FCS,网卡通常把它剥掉不给上层,所以抓包看到 60。 3. MSS 是怎么算出来的? —— MTU 减去 IP 头 20 与 TCP 头 20,以太网下是 1460。

路上走完了。接下来单独看这条路的三段:IP、TCP 和 DNS。

相关笔记