
导语:本单元着重讲解传输层。主要目的是了解网络通讯中面临的数据传输问题,以及 TCP 和 UDP 等协议是如何在不可靠的网络层之上,构建出满足应用需求的传输服务的。
Main Topics
- 三种广泛使用的协议:
- TCP:应用间字节流的可靠传输。
- UDP:应用间数据报的不可靠传输。
- ICMP:检测网络何时出问题。
- TCP 的工作原理:滑动窗口 (Sliding Window)、Go-back-N、Selective repeat、TCP 状态机。
- 端到端原则 (End to End):网络架构的核心哲学。
- 错误检测方法:3 种保障数据安全与完整性的机制。
1. The TCP Service Model (TCP 服务模型)
TCP (Transmission Control Protocol) 为应用程序提供了按顺序的、可靠的、端到端的字节流交付服务。
1.1 Transmission Control Protocol (TCP)
- 对等通信 (Peer communication):TCP 在通信双方的传输层之间建立对等连接。
- 三次握手 (3-Way Handshake):TCP 的通讯必须通过三次握手来建立可靠连接:
- SYN:A 向 B 发送同步信号,并携带一个起始序列编号 (ISN)。
- SYN + ACK:B 收到后,返回一个同步信号 (携带 B 自己的起始编号),并确认 (ACK) 接收到了 A 的请求。
- ACK:A 发送 ACK,确认接收到了 B 的反向通讯请求,连接正式建立。
1.2 TCP 字节流服务 (Byte Stream Service)
- 字节流是一个连续的字节时间序列。TCP 将应用程序需要发送的常见数据统统看作“字节流”。
- 在网络传输时,TCP 会将连续的流 (Stream) 分割为一个个独立的段 (Segment)。
- 如果发送的某个段在规定时间内没有收到接收方的回应 (ACK),TCP 会选择简单粗暴的策略:重发。
1.3 Connection teardown (连接拆除)
连接的关闭同样需要经过严谨的握手确认过程:
- FIN:A 发送 Finish 报文,表示 A 已经没有数据要发送了。
- ACK (+ Data):B 收到后回应 ACK。此时 B 可能还有没发完的数据,所以可以继续发送新数据,连接处于“半关闭”状态。
- FIN:等 B 也发送完毕后,B 发送 Finish 报文。
- ACK:A 发送 ACK,确认 B 的关闭请求,连接最终安全关闭。
1.4 Model 表现 (模型特性)
TCP 究竟提供了一个什么样的传输服务?
- 可靠传输:利用 ACK 表示确认接收。
- 错误校验:利用 Checksums (校验和) 检测数据是否损坏。
- 序列号 (Sequence):利用序列号(一开始发送的起始编号)进行数据排序和丢包检测。
- 流量控制 (Flow Control):防止双方过载。如果 A 发送过快而 B 处理不过来,TCP 会动态调整,确保双方都在可承受范围内发送数据。
- 拥塞控制 (Congestion Control):感知整个网络的拥堵情况,避免网络崩溃。
1.5 TCP Segment Format (TCP 数据段格式)
TCP 数据段会被封装在 IP 数据包内,体现了网络分层的封装原则:
| IP Hdr | TCP Hdr | TCP Data |
TCP Header (头部) 包含以下核心字段:
- Source port / Destination port:源端口与目标端口(标识具体的应用程序进程)。
- Sequence:序列号。
- Ack Sequence:确认号。
- Checksum:校验和。
- Window size:窗口大小(用于流量控制)。
- Urgent pointer:紧急指针标识。
- (TCP Options):其他附加选项(如最大段大小 MSS)。
1.6 TCP Unique ID (唯一标识)
主机 A 会使用递增的随机端口来标识新的连接。TCP 会挑选一个随机的初始序列号 (ISN, Initial Sequence Number) 来避免和历史遗留的旧连接 ID 或序列号混淆。
1.7 Sequence Numbers (序列号)
序列号告诉主机当前数据段在整个字节流中的起始值(即下一次传输的偏移量)。通过对比期望的序列号和实际收到的序列号,接收方能进行初步的丢包和乱序校验。
1.8 TCP Sliding Window (滑动窗口)
为了提升传输效率,TCP 采用了滑动窗口技术(在第 8 节中会有详细讲解)。
2. The UDP Service Model (UDP 服务模型)
UDP (User Datagram Protocol,用户数据报协议) 是提供给那些不需要严格可靠性保证的应用使用的协议。
2.1 UDP Datagram Format (UDP 数据报格式)
UDP 的头部极其精简,只有短短 8 个字节:
- Source port 和 Destination port。
- Length:至少为 8 字节(即 UDP 头部的最小长度)。
- Checksum (optional):校验和是可选的,如果使用,会覆盖部分 UDP 数据头来进行完整性校验。
2.2 UDP: Port Demultiplexing (端口多路分解)
UDP 可以看作是一个多路分解协议:它仅仅是将接收到的 IP 数据包,根据目标端口号 (Port) 分发给对应的应用程序。
2.3 UDP 表现 (特性与应用)
- 特性:无连接建立 (无需握手),Packets 可能会以任何顺序到达(但应用层可以自己在包里加序列号来重新排序)。
- Unreliable delivery (不可靠交付):没有 ACK,没有丢包检测机制 (无 Window size 和 Sequence),也没有流量控制 (Flow control)。
- 为什么还要用 UDP? 因为它极度轻量、快速,开销极小。非常适合提供简单的 Request-Response 基础服务,或者对实时性要求极高、能容忍偶尔丢包的场景。
- 经典应用:DNS (Domain Name System, 域名系统) 查询。
3. The ICMP Service Model (ICMP 服务模型)
ICMP (Internet Control Message Protocol,互联网控制消息协议) 用于报告网络错误和诊断问题。虽然它被封装在 IP 数据包中(看似在传输层之上),但它本质上是为网络层服务的。
3.1 网络层的工作职责
- IP 协议:负责将 IP 数据包逐跳传输。路由器利用特定的路由算法生成路由表,并据此填充转发表来进行数据转发。
- ICMP 协议:作为 IP 的副手,负责帮助 Host 和 Router 之间交流,报告错误 (Error),以及找出出问题的数据包路径。
3.2 ICMP run above the Network Layer
在网络连接出现问题时,ICMP 发挥关键作用。
- EXAMPLE: 目标不可达 (Destination Network Unreachable):如果数据包到达某个路由器,但目标地址在该路由器的转发表中不存在,路由器就会丢弃该包,并向上游发送一个 ICMP 报错信息。
3.3 ICMP 表现
- Reporting Message:自带报错的类型和代码信息。
- Unreliable:ICMP 自身也是简单的、不可靠的数据报服务。如果不幸 ICMP 报错包也丢了,它不会重试 (no retries)。
3.4 ICMP Message Type (消息类型)
不需要死记硬背,用到时查阅 Internet RFC 792 即可。常见类型:
Type 0 - Code 0:Echo Reply (回显应答)。Type 3 - Code 0:Destination Network Unreachable (目标网络不可达)。Type 11 - Code 0:TTL Expired (生存时间超时)。
3.5 How “ping” uses ICMP (Ping 工具原理)
ping 命令用来检查本机与目标主机是否有网络连接。它不涉及 TCP/UDP,而是直接调用 ICMP,发送一个 ICMP Echo Request (回显请求),目标主机收到后返回 Echo Reply。
3.6 How “traceroute” uses ICMP (Traceroute 工具原理)
traceroute 告诉我们数据包跳跃经过的具体路径节点。
- 实现原理:通过不断发送 TTL(生存时间,表示最多经过几跳)递增的 UDP 数据包(TTL=1, 2, 3…)。
- 当数据包到达第一跳路由器时,TTL 减为 0,路由器丢弃该包并返回一个 ICMP 11 (TTL Expired) 报错。
traceroute捕获这个报错,就知道了第一跳路由器的 IP 地址。依次类推,直到到达最终目标。
4. The End-to-End Principle (端到端原则)
这是互联网设计中最核心的哲学:强调“正确性”的强端到端原则。
4.1 Application View of the World (应用的世界观)
应用程序进行网络通信非常简单,只需调用基础的 Socket (套接字) 即可完成。网络底层默默提供了很多帮助:数据压缩、格式化、甚至硬件级的安全加密和移动性支持。
但是,为什么底层网络不包揽一切(比如保证数据绝对不错不丢)?
因为网络只能提供“帮助”,而不能保证业务逻辑的最终“正确性”。真正的正确性,必须依靠终端(端到端)自己来校验。
4.2 Example: File Transfer (文件传输例子)
在文件传输时,每一跳的路由器虽然都有错误检测 (Error detection),但这只检测了线路传输过程中的错误,而检测不到内存存储中的错误(比如路由器内存坏了导致比特反转)。
因此,确保文件正确无误送达的唯一方式,是在接收端对整个文件进行校验(即端到端原则):网络可以帮忙优化,但绝不能完全保证正确。
4.3 Example: Link Reliability (链路可靠性)
链路层提供的帮助极大提升了性能:
- 有线链路:非常可靠,包正确发送率达 99.99%。
- 无线链路:极易受干扰,原生包正确率仅 50~80%。
链路层可以在中间层通过物理重传等优化手段,将无线链路的成功率提升到 99%。底层优化是有价值的,但依然无法避免极端情况下的失效。
4.4 “Strong” End to End (强端到端)
复杂的逻辑不应在网络中间层实现,只应在网络边缘(计算机终端)执行。
- 强调网络的简洁性。如果在中间路由器做太多特定业务优化,不仅不一定总是正确,还会导致网络僵化 (Network Ossification),使得未来的底层网络难以升级和改变。
5. Error Detection: 3 schemes (错误检测的三种方案)
网络中的数据包需要添加冗余位来识别错误,主要有三种常见机制。我们需要了解其差异,从而在系统设计时做出正确的协议决策。
5.1 Error Detection (错误检测概览)
通过在数据的头部或尾部加入校验码。常见的如 Checksum、CRC、TLS MAC。
5.2 Checksum (校验和)
- 特点:快速 (fast) 且计算成本低 (cheap),但鲁棒性较差 (not very robust,较脆弱)。
- 实现:IP / UDP / TCP 使用的是 16位反码求和 (one’s complement checksum algorithm)。对比特位进行简单累加运算作为数据包的校验和。
5.3 Cyclic redundancy code (循环冗余校验码, CRC)
- 特点:计算成本高于 Checksum,但鲁棒性极好,能检测更多复杂的连续错误。如今在硬件(特别是链路层 Link Layer)中极易实现。
- 主要思想:将 nnn bits 的数据经过多项式除法,提取为 ccc bits 的特征码(其中 c≪nc \ll nc≪n)。
- 极小概率出现碰撞(两个不同的包算出同样的 CRC,概率为 2−c2^{-c}2−c)。
5.4 Message authentication code (消息认证码, MAC)
- 特点:强大的密码学原理,只有拥有秘密信息(密钥)的人才能生成和查看信息。对“恶意篡改”具有极强的抵抗力。
- 注意:千万不要与网络硬件的 MAC 地址 (Media Access Control) 混淆!如果你更改了信息的任何一个 bit,将导致整个特征码雪崩式改变。不过,它主要用于防篡改,用于常规通信噪音的“错误检测能力”不如 CRC 高效。
5.5 Quiz (检测能力小结)
- 8/16位 Checksum:能检测 1 bit 错误;但不能必然检测 2 bit 错误、9 bit 错误,或者相隔很远(比如 100 bits)的抵消错误。
- 8/16位 CRC:能完美检测 1 bit、连续的突发错误;8 bits CRC 无法检测某些 2 bit 错误,但 16 bits 可以。不过它们都无法保证检测出相隔很远的随机错误。
- 32位 MAC:它的设计目的不是为了常规的信道纠错,所以单从纠正信道噪音的角度看,“不能保证检测到特定的物理噪音错误”,它只保证数据的不可伪造性。
6. Finite State Machines (有限状态机, FSM)
网络协议的运转逻辑极其复杂,因此引入有限状态机 (FSM) 进行建模。系统由有限的“状态 (State)”组成,状态之间的“边”定义了触发的“事件 (Event)”和采取的“行动 (Action)”。系统同一时刻必定且只能处于一种确定状态。
6.1 FSM Example: HTTP Request
一个简单的 HTTP 请求事件会导致系统进入不同的状态。
- 例如,发起请求后等待响应,根据不同的响应结果切换到结束状态或错误状态。
- 协议设计者必须考虑极其严谨的具体情况:如何处理空闲状态的特殊请求?如何处理 Close Connection?如果出现了未定义状态或非法操作该怎么办?
6.2 FSM Example: TCP Connection
TCP 连接的完整生命周期就是一个典型的复杂有限状态机,状态转移类似于数字电路中的状态转移图。
(注:这里梳理了三次握手与四次挥手背后的理论模型,也是本单元最需要花时间手动画图推演的部分,具体状态机详解见第 11 节。)
7. Flow Control - Stop and Wait (流量控制:停等协议)
当通信双方的收发数据能力不对等时,流量控制 (Flow Control) 是实现高效通信的重要因素。
- 核心目标:发送方的发送速度绝对不能超过接收方的处理能力。
- 机制:接收方必须给发送方提供显性(ACK 确认)或隐性(超时)的反馈。
7.1 Stop and Wait (停等协议)
- 基本算法:发送方发送一个包,然后停下来,死死等待接收方发来 ACK 确认后,才会发送下一个数据包。如果长时间没有回复,就超时重发。
- 致命问题:如果网络出现延迟,发送方超时重发了 Data,接收方收到了两份 Data,如何区分这是新数据还是旧数据?
- 解决方案:引入 1 bit 的计数器 (cnt = 0 或 1)。标记
Data 0对应ACK 0,Data 1对应ACK 1,交替进行。但这依然无法解决网络链路极长时,效率极其低下的问题。
8. Flow Control - Slip Window (流量控制:滑动窗口)
前情提要:Stop and Wait (SaW) 算法效率低下,因为它无法充分利用网络容量。
网络传输存在两个物理限制:
- Bottleneck (传输瓶颈):如 10 Mbps,代表接收方每秒的最大接收数据量。
- RTT (往返时间):数据跑个来回需要的时间。
滑动窗口技术通过允许发送方并发发送多个数据包来填满网络管道。
8.1 Sliding Window Sender (发送端窗口)
每一段数据都有一个序列号 (SeqNo)。发送方维护着三个核心变量:
- SWS (Send Window Size):发送窗口大小(允许同时在路上跑的最多包数量)。
- LAR (Last Acknowledgment Received):接收方最后一次确认的位置编号。
- LSS (Last Segment Sent):发送方最后一个发出的段的位置编号。
发送方必须严格维护一个不等式:LSS−LAR≤SWSLSS - LAR \le SWSLSS−LAR≤SWS(即:最后一个发送 - 最后一个接收确认 ≤\le≤ 窗口大小)。只有当收到新的 ACK 更新 LAR 时,窗口才能向前滑动。
8.2 Example (接收端示例)
- RWS (Receive Window Size):接收方缓存的大小,只要在这个窗口内的包,接收方都可以先收下缓存。
- 累计确认 (Cumulative Acknowledgment):注意,TCP 不是收到第 nnn 个包就确认第 nnn 个包。TCP 发送
ACK n,意思是:“我已经完美接收了序列号 n−1n-1n−1 及之前的所有数据,现在期待收到 nnn。” - 如果中间丢了包,接收方会连续发送相同的最后接收数据 ACK(冗余 ACK),来隐式提醒发送方丢包了。
9. Retransmission Strategies (重传策略)
当滑动窗口中某个包丢失时,传输协议需要根据 SWS 和 RWS 的大小等因素,小心地选择重传策略。
9.1 Go-Back-N (回退 N 步重传)
- Pessimistic behavior (悲观策略):如果序列号为 nnn 的数据包丢失,即使接收方已经完美收到了 n+1,n+2n+1, n+2n+1,n+2,接收方也会将其全部丢弃。
- 发送方一旦发现 nnn 丢失,会停止发送新数据,回退到 nnn,并把 nnn 和之后的所有包全部重传一遍,就像事件倒流一样。
- 这种策略通常在接收方窗口大小 (RWS) 为 1 的简单设备上使用。
9.2 Selective Repeat (选择性重传)
- 乐观策略:只精确重新传那个没有收到 ACK 的特定的包。接收方如果收到乱序的包,会将其暂时存在 RWS 缓存中,等缺失的那个包重传到了,再按顺序拼装上交。
- 缺点:如果有大量连续数据包丢失,选择性重传逐个挑着重发的效率,实际上比 Go-Back-N 的整批重发还要慢。
10. TCP Header (TCP 头部详解)
TCP 头部标准长度为 20 字节(即 5 行 32 bits / 4 octets),包含极其丰富的信息控制字段:
- Source port / Destination port
- Sequence number:当前数据段的字节偏移量。
- Acknowledgment number:期望收到对方下一个段的序列号。
- Offset:数据偏移,告诉你 TCP 报头有多长(因为 Options 是可变长的)。
- Reserved:保留位。
- 核心控制位 (Flags):
- U (Urgent bit): 与下方的 Urgent pointer 配合,指向紧急数据。
- A (Ack bit): 确认号是否有效(除了第一次建立连接的 SYN,后续所有的包 A 位都是 1)。
- P (Push bit): 提示接收方尽快把数据推送给应用层。
- R (Reset): 发生了严重错误,强制重置连接。
- S (SYN): 建立连接时使用,需要告诉对方初始序列号。
- F (FIN): 数据发送完毕,请求拆除链接。
- Window:告诉对方自己的接收窗口还剩多少空间(用于流量控制)。
- Checksum:校验和。
- Urgent pointer / Options / Padding。
10.1 安全性
为什么三次握手时不固定从序列号 0 开始,而是使用随机的 ISN?
因为如果每次序列号规则固定,恶意攻击者很容易猜测出你的序列号,然后伪造 IP 源地址,将含有恶意代码的数据包注入到你的 TCP 连接中。随机 ISN 大大降低了这种盲打注入的风险。
11. TCP Setup and Teardown (TCP 建立与拆除详解)
结合 TCP 头部的 Flags,进一步剖析 TCP 状态机中的握手细节。
11.1 3-way Handshake (三次握手)
- 建立连接,发送方是 Active (主动) 的,接收方(服务器)是 Passive (被动) 的。
- 双方必须知道对方监听的端口号。
- 过程:发送
SYN-> 对方响应SYN + ACK-> 本机确认ACK。这三步不仅确认了双向通信通畅,还互相交换了随机化的起始序列号。
11.2 Simultaneous open (同时打开现象)
如果通信双方极为巧合地同时向对方发起主动连接(都是 Active),双方都会先发送 SYN,接收后都会回复 SYN+ACK。此时连接建立过程会演变成罕见的**“四次握手”**,但连接依然可以正常建立。
11.3 Connection Teardown (连接拆除:四次挥手)
因为 TCP 是全双工的,Both sides must send FIN to terminate a connection(双方都必须独立发送 FIN 来终止自己方向的连接)。
- A -> B: 发送
FIN(seq SA), 附带ACK SB。 - B -> A: 收到后确认,发送
ACK SA+1。 - (B 继续发完自己剩下的数据)
- B -> A: 发送
FIN(seq SB), 附带ACK SA+1。 - A -> B: 收到后确认,发送
ACK SB+1。 - (确认序列号 (seq) 的方式,一般是对方的期望 Ack = 当前 Seq + 1;确认 FIN 的方式也是发送对该 FIN 的 ACK,双方甚至可以并行发送 FIN)。
11.4 Cleaning Up Safely (安全地清理连接)
A (主动关闭方) 在发送了最后一个 ACK 之后,绝对不能马上销毁连接并释放资源!
- 问题:如果这最后一个 ACK 丢了怎么办?B 迟迟收不到确认,就会疯狂重发最后一个 FIN。如果 A 已经销毁了连接,就会回应 RST 错误包。
- 解决:主动关闭者必须进入一个 TIME_WAIT 状态,等待 2MSL (Maximum Segment Lifetime,最大的段寿命的两倍时间)。这确保了网络中所有残留的延迟包都死干净,也确保了 B 能收到最后的 ACK(如果丢了,在这个时间内还能响应 B 重发的 FIN)。
11.5 三次握手 关闭连接
如果 B 收到 A 的 FIN 时,自己刚好也没有数据要发了,B 可以把自己的 FIN 捎带在 ACK 一起发过去,此时四次挥手就可以被优化压缩为三次挥手:
- 发送
FIN0。 - 确认
FIN0并发送自己的FIN1(合并发送)。 - 确认
FIN1。
11.6 Full TCP FSM (完整的 TCP 有限状态机)
整合上述的所有过程,就构成了计算机网络中最经典的 12 种状态图谱:
- 建连阶段:
LISTEN->SYN_SENT/SYN_RCVD->ESTABLISHED。 - 断连阶段 (主动):
FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED。 - 断连阶段 (被动):
CLOSE_WAIT->LAST_ACK->CLOSED。
自己拿纸笔推演并画一次这个有 12 个状态的转移图,是彻底掌握 TCP 最好的练习!


被折叠的 条评论
为什么被折叠?



