🕸️ 第三章 为Web提供支持的技术
📄 一句话概括

上网这件事被拆成四层来干,每层只管自己那一小段;层层套壳之后,一段文字才变成能穿越大半个地球的包裹。

📝 本节和前面几节的关系

3-1 列了一张支撑技术的全景表,这一节从表里最底下的那一层讲起:TCP/IP 是一整套协议族,不是单个协议。它是后面 DNS、DHCP、TLS 全部站上去的地基。

一、为什么要分层

如果全世界的网络只用一套规则,那么任何一处改动都要全世界跟着改——换个网卡技术、换个传输方式,都得重新设计一遍。

分层的思路是把问题切开:每一层只管自己的那一小段,层与层之间只约好接口。这样换掉某一层的东西,其他层不用动。这也是网络能从几十台机器长到今天这个规模的原因。

二、四层各自的本职

TCP/IP 通常分四层,从下往上分别是链路层、网络层、传输层、应用层。

链路层管【这一跳】:在相邻的两台设备之间把数据送过去,典型协议是以太网和 Wi-Fi。

网络层管【从起点到终点】:给每台机器编号并按编号找路,主角是 IP。它只负责尽力送达,不保证顺序、也不保证不丢。

传输层管【完整有序】:TCP 把大块数据切成段、编上号,丢了就重发;UDP 则相反,图快不图全。

应用层管【给用户提供什么服务】:HTTP 取网页、DNS 查名字、FTP 传文件、SMTP 发邮件,全在这一层。

三、动手 Demo:套壳的账与等待的账

数据不是凭空过去的,它每往下走一层就要加一段头。这段头不多,但架不住层层都加。

head = {"HTTP 头": 400, "TCP 头": 20, "IP 头": 20, "以太网头": 14, "帧校验": 4}
body = 1000
total = body + sum(head.values())
print("净荷", body, "字节,实际发出", total, "字节")
print("开销 %d 字节,占 %.1f%%" % (sum(head.values()), sum(head.values()) * 100.0 / total))
print("净荷占比 %.1f%%" % (body * 100.0 / total))

实跑输出:

--------------------------------------------------------------------
D2  TCP/IP:四层怎么分工、每层加多少头
--------------------------------------------------------------------

  TCP/IP 不是单个协议,而是一整套。它把活分成四层,每层只管自己那一摊,
  上一层的数据往下走的时候,每一层都要给它套一层壳。

  层              本职                                      典型协议
  --------------------------------------------------------------------------------------
  应用层          决定给用户提供什么服务                    HTTP、DNS、FTP、SMTP
  传输层          把数据切成段、保证完整有序                TCP、UDP、QUIC
  网络层          给每台机器编号并按编号找路                IP、ICMP、ARP
  链路层          在相邻两台设备之间传这一跳                以太网、Wi-Fi

  发一条 1000 字节的正文,一路上要套几层壳:

  层次              加了什么                      字节          累计
  ----------------------------------------------------------------------------
  正文              要传的内容本身                1000          1000
  应用层            HTTP 头(含 Cookie 等)       400           1400
  传输层            TCP 头(端口、序号)          20            1420
  网络层            IP 头(源与目的地址)         20            1440
  链路层            以太网头(MAC 地址)          14            1454
  链路层            帧校验(FCS)                 4             1458

  净荷 1000 字节,实际发出去 1458 字节
  多出来的壳 458 字节,占 31.4%
  真正有用的部分只占 68.6%

  壳看着多余,但正是它让【一段文字】变成了【能穿越大半个地球的包裹】。

  再说【等多久】。TCP 要先把连接建立起来才能传数据,这一来一回的耗时叫 RTT:

  场景                  一次往返              建连接要多久
  ------------------------------------------------------------------
  同一个局域网          1 ms                  1 ms
  同一个城市            10 ms                 10 ms
  跨省                  30 ms                 30 ms
  跨太平洋              150 ms                150 ms

  注意:这只是建立连接。如果还要走 HTTPS,加密握手起码再来一次往返。

两笔账值得记住。第一笔是套壳的账:1000 字节的正文,发出去变成 1458 字节,多出来的 458 字节全是头,占了三成。头看着浪费,但它正是【一段文字】能变成【能穿越大半个地球的包裹】的原因。

第二笔是等待的账:TCP 要先把连接建立起来才能传数据,这一来一回的耗时叫 RTT。同一个局域网 1 毫秒无所谓,跨太平洋 150 毫秒就肉疼了——因为后面还要握手,而握手是按【往返次数】算的。

四、用一层的眼光想问题

分层不只是画图好看,它直接决定了排查和优化的方向。

网站打不开,先按层往下问:域名能不能解析(应用层)、端口通不通(传输层)、能不能 ping 到(网络层)、网线灯亮不亮(链路层)。一层一层测下来,故障点很快就锁定了。

优化也一样:想减少等待,压缩的是往返次数;想减少体积,动的是头的数量。知道自己在哪一层动刀,才不会做无用功。

30 秒实验

  1. 在命令行运行 ping 8.8.8.8,看输出里的 time=xx ms——那就是一次往返的耗时,也是握手耗时的基本单位。
  2. 运行 tracert www.baidu.com(Windows)或 traceroute(macOS / Linux),数一数从你家到服务器一共经过路由器几跳。
  3. 在开发者工具的 Network 面板里看 Connection 那一列写的是 h2 还是 h3,这能看出你走的是 TCP 还是 QUIC。

常见误区

  1. 错误:TCP/IP 是一个协议。——纠正:它是一整套协议族,TCP 和 IP 只是其中最有名的两个。同一个筐里还装着 UDP、ICMP、ARP、HTTP、DNS 等等。把它当成【一整套规则】,才不会到处找不到【那个叫 TCP/IP 的协议】。
  2. 错误:分层只是为了分类好看。——纠正:分层的真正价值是可替换。在 IP 之上既可以用 TCP,也可以用 UDP 和 QUIC——HTTP/3 就是靠这一点才换得动传输层。
  3. 错误:每层加的那些头是浪费。——纠正:每段头都在解决一类问题:端口决定交给哪个程序、IP 决定送到哪台机器、MAC 决定交给哪块网卡。去掉任何一段,对应那件事就没人干了。
  4. 错误:TCP 一定比 UDP 好。——纠正:它们服务的目标不一样。TCP 要的是【一个字节都不少】,UDP 要的是【别等】。直播、语音这种【迟到的数据没用】的场景,反而应该选 UDP。
自测三问(点开对答案) 1. 四层各自解决什么问题? —— 链路层解决【这一跳怎么送】,网络层解决【从起点到终点怎么找路】,传输层解决【数据完整有序】,应用层解决【给用户什么服务】。**记的时候按【一跳、一路、一段、一层服务】来记**,比背协议名管用。 2. 1000 字节的正文为什么会变成 1458 字节? —— 因为每往下走一层就要加一段头:HTTP 头 400 字节、TCP 头 20 字节、IP 头 20 字节、以太网头 14 字节、帧校验 4 字节,**加起来 458 字节**。**这个比例说明:小文件传输的开销占比会特别难看**——所以小图片要合并、要内联。 3. 为什么跨洋访问对握手次数特别敏感? —— 因为**握手是按往返次数算的**,一次往返 150 毫秒。TLS 1.2 要 2 次、TLS 1.3 只要 1 次,**差的就是实打实的 150 毫秒**。距离没法缩短,能缩的只有往返次数。

相关笔记