问题?

为什么刷网页、传文件时网络断一下就会加载失败,需要重连;而打游戏、看直播时,就算偶尔卡一下、丢几帧,画面却能很快跟上,不会整个停了。

这个就要看网络传输层的两大核心协议了:TCP 和 UDP。

这篇文章从基础定义出发,一步步深入到连接管理、可靠传输、流量拥塞控制、粘包问题等核心,构建完整的知识体系。

一、先搞懂:传输层到底在做什么?

我们要弄明白一个核心问题:传输层在整个 TCP/UDP 协议栈里,扮演了什么角色?

都知道 IP 协议负责把数据包从一台主机送到另一台主机,但 IP 只认 IP 地址,不认应用程序。
一台设备上同时跑着浏览器、游戏、微信等,数据包到了之后该交给谁?

这就是传输层的核心价值:实现端到端的进程间通信,通过端口号唯一标识不同的应用程序。

端口号是 16 位的数字,总范围 0~65535,分三类:

  • 周知端口(0~1023):固定分配给标准服务,如 HTTP 用 80、HTTPS 用 443、DNS 用 53
  • 注册端口(1024~49151):分配给普通用户进程或自定义服务
  • 动态/临时端口(49152~65535):客户端发起连接时随机分配,连接关闭后回收

在进程寻址这个基础上,传输层还可以选择是否为数据传输提供质量保障。因此产生了两种设计哲学完全相反的协议:

  • 稳妥至上:不仅要把数据送到,还要保证不丢、不乱、不重复,哪怕慢一点也没事 —— 这就是 TCP
  • 效率至上:只管把数据发出去,到不到、快不快全看网络,能快一点是一点 —— 这就是 UDP

二、TCP 协议深度解析

TCP 全称 Transmission Control Protocol(传输控制协议),由 RFC 793 标准定义,它的一切设计都围绕可靠展开。

可以把 TCP 通信理解为打电话:

  • 通话前必须拨号、等待对方接通,确认双方都能听能说,才开始交流
  • 说话时要确保对方听清,重要的内容要等对方回应,听不清还要重复一遍
  • 通话结束后要互相道别,确认没有内容了再挂断

2.1 核心特性详解

1. 面向连接

TCP 是面向连接的协议,通信双方在传输数据前,必须通过三次握手建立一条逻辑连接(并非物理电路);数据传输结束后,通过四次挥手释放连接。

连接建立后,双方会维护完整的连接状态(包括序号、窗口大小、缓冲区状态等),全程追踪数据传输情况。

2. 可靠交付

TCP 通过一整套机制,保证数据不丢失、不重复、按顺序到达,上层应用完全感知不到底层网络的波动。

  • 字节级序号:TCP 把传输的数据按字节编号,每个字节都有唯一的序号;SYN(连接建立)和 FIN(连接断开)报文本身也占用一个序号。
  • 累计确认:接收方通过确认号告知发送方:该序号之前的所有数据都已经成功接收,期望收到下一个序号的数据。一次确认可以确认多段数据,效率更高。
  • 超时重传:发送方发出数据后启动计时器,超时未收到确认则重传对应数据。超时时间 RTO 会根据网络延迟动态调整,适配不同网络环境。
  • 选择确认(SACK):可选扩展功能,接收方可以告知发送方哪些数据收到了、哪些丢了,发送方只重传真正丢失的片段,避免不必要的全量重传。
  • 差错校验:通过首部校验和检测报文传输错误,出错则直接丢弃,不返回确认。
  • 乱序重排:接收方对乱序到达的数据按序号缓存排序,等缺失片段补齐后,再有序交付给应用层。

3. 面向字节流

TCP 将应用层提交的数据视为无结构的字节流,不保留消息边界。发送端可以根据缓冲区情况拆分、合并数据发送,接收端按字节顺序重组后交付。

粘包问题:正是因为 TCP 不保留消息边界,接收方无法区分原始数据的发送分段,可能出现“一次收到多条消息”或“一条消息分多次收到”的情况,这就是常说的粘包/拆包问题。

常见解决方案有三种:

  1. 固定消息长度:每条消息长度固定,接收方按固定长度读取
  2. 特殊分隔符:用换行符、特定字符作为消息边界
  3. 消息头+长度字段:头部指定消息体长度,接收方先读头再按长度读数据(最常用)

4. 流量控制(滑动窗口)

流量控制解决的是「发送方发太快,接收方处理不过来」的问题,核心是滑动窗口机制。

接收方在 TCP 头部的「窗口大小」字段中,实时告知发送方自己当前可用的接收缓冲区大小。发送方根据该值调整发送数据量,确保发送速率不超过接收方的处理能力。

  • 当接收方缓冲区满时,会将窗口大小设为 0,发送方停止发送
  • 发送方会定期发送零窗口探测报文,询问接收方是否有了新的缓冲区空间

5. 拥塞控制

拥塞控制解决的是「网络太堵,过多数据注入导致全网瘫痪」的问题,核心是动态调整发送方的拥塞窗口(cwnd),由四个经典算法组成:

  1. 慢启动:连接建立后,拥塞窗口从 1 个 MSS 开始,每经过一个 RTT 翻倍(每收到一个 ACK 增加 1 个 MSS),快速探测网络带宽
  2. 拥塞避免:当窗口达到慢启动阈值(ssthresh)后,改为线性增长(每个 RTT 增加 1 个 MSS),平稳提升速率,避免触发网络拥塞
  3. 快重传:收到 3 个重复的确认号时,立即重传丢失的报文,无需等待超时计时器到期
  4. 快恢复:触发快重传后,将慢启动阈值减半,拥塞窗口直接设为新阈值,进入拥塞避免阶段,避免速率骤降

2.2 连接管理机制深度讲解

三次握手(建立连接)

三次握手的核心目的:同步双方的初始序号、确认双方收发能力正常、避免失效连接浪费资源。

完整过程与状态变化:

  1. 客户端向服务端发送 SYN 同步报文,携带初始序号 seq=x,客户端进入 SYN_SENT 状态
  2. 服务端收到后,回复 SYN+ACK 报文,携带自身初始序号 seq=y,确认号 ack=x+1,服务端进入 SYN_RCVD 状态
  3. 客户端收到后,回复 ACK 确认报文,seq=x+1,ack=y+1,双方进入 ESTABLISHED 状态,开始传输数据
为什么是三次,而不是两次?
  • 为了同步双方的初始序列号:双方都需要确认对方收到了自己的初始序号,两次握手只能保证单向同步
  • 防止已经失效的连接请求突然到达服务端,导致服务端错误建立连接、浪费资源

SYN 洪水攻击就是利用这个机制,攻击者伪造大量 SYN 报文,不回复第三次握手,导致服务端半连接队列耗尽,无法响应正常请求。防护方式有 SYN Cookie、调整半连接队列大小等。

四次挥手(断开连接)

TCP 是全双工通信,收发通道是独立的,需要分别关闭,因此需要四次交互。

完整过程与状态变化:

  1. 主动关闭方发送 FIN 结束报文,告知对方“我没有数据要发了”,进入 FIN_WAIT_1 状态
  2. 被动关闭方回复 ACK 确认,进入 CLOSE_WAIT 状态;主动方收到后进入 FIN_WAIT_2 状态,此时被动方仍可继续发送剩余数据
  3. 被动方数据发送完毕后,发送 FIN 结束报文,告知对方“我也发完了”,进入 LAST_ACK 状态
  4. 主动方回复 ACK 确认,进入 TIME_WAIT 状态,等待 2MSL(最长报文寿命)后彻底关闭;被动方收到 ACK 后直接关闭连接
TIME_WAIT 为什么要等 2MSL?
  • 保证最后一个 ACK 报文能到达对方:如果 ACK 丢了,被动方会重发 FIN,主动方还有时间重传 ACK
  • 让旧连接产生的重复报文在网络中自然消散,避免干扰之后使用相同端口的新连接

2.3 TCP 报文头部详解

TCP 报文由头部和数据两部分组成,头部固定长度 20 字节,另有最长 40 字节的选项字段,总头部最大 60 字节。

字段 长度 作用
源端口/目的端口 各 16 位 标识通信双方的应用进程,实现进程间寻址
序号(SEQ) 32 位 本报文段数据第一个字节的序号
确认号(ACK) 32 位 期望收到对方下一个报文段第一个字节的序号
数据偏移 4 位 指示 TCP 头部总长度,以 4 字节为单位
标志位 6 位 SYN(建连)、ACK(确认)、FIN(断连)、RST(重置)、PSH(推送)、URG(紧急)
窗口大小 16 位 接收方的接收能力,用于流量控制
校验和 16 位 校验报文段传输是否出错,包含伪首部计算
紧急指针 16 位 标识紧急数据的末尾位置
选项字段 0~40 字节 可选,如 MSS、窗口扩大因子、时间戳、SACK 等

2.4 典型应用场景

TCP 适用于对数据完整性、准确性要求高,对实时性要求相对宽松的场景:

  • 网页浏览:HTTP/HTTPS
  • 文件传输:FTP、SFTP
  • 远程登录:SSH、Telnet
  • 邮件传输:SMTP、POP3、IMAP
  • 数据库访问、远程桌面、支付接口等

三、UDP 协议深度解析

UDP 全称 User Datagram Protocol(用户数据报协议),由 RFC 768 标准定义,它的设计思路和 TCP 完全相反:极简、高效、不做多余的事。

如果说 TCP 是打电话,那 UDP 就是寄明信片:

  • 写好地址、贴好邮票,直接扔进邮筒就行,不需要提前确认对方在不在家
  • 明信片在路上会不会丢、会不会迟到、会不会先寄的后到,邮局不负责,你也管不了
  • 对方收到了不会特意通知你,没收到你也不会收到提醒

3.1 核心特性详解

1. 无连接

通信前不需要建立逻辑连接,发送方随时可以发送数据;结束后也不需要挥手释放连接,没有任何连接状态的维护。启动快、开销小,没有握手延迟。

2. 不可靠交付

没有确认应答、超时重传、乱序重排机制。数据发出去后,不关心对方是否收到、是否完整、是否按顺序到达,丢包、乱序、重复都不会做任何处理,也不会通知发送方。

3. 面向数据报

UDP 对应用层交付的完整报文原样封装,一次发送一个完整数据报;接收方也一次接收一个完整数据报,严格保留消息边界,不存在 TCP 的粘包问题。

4. 无流量控制与拥塞控制

发送速率只由应用程序和硬件性能决定,不会因为接收方处理能力不足或网络拥堵而主动降速。

  • 优点:延迟稳定、波动小,适合实时性场景
  • 缺点:网络拥塞时不会放缓发送,可能进一步加剧网络拥堵和丢包

5. 首部开销极小

UDP 首部固定只有 8 字节,远小于 TCP 的 20 字节固定首部,额外传输成本极低。

UDP 的校验和同样会基于「伪首部+UDP 首部+数据」计算,用于检测传输差错;IPv4 中校验和是可选的,IPv6 中强制开启。

6. 支持多种通信模式

不仅支持一对一单播通信,还天然支持:

  • 广播:向同一局域网内所有主机发送数据
  • 组播(多播):向加入了特定组播组的主机发送数据,实现一对多高效分发

3.2 UDP 报文首部结构

UDP 报文由 8 字节固定首部 + 数据部分组成,首部仅 4 个字段,每个字段占 16 位:

字段 长度 作用
源端口 16 位 发送方进程端口,不需要回复时可填 0
目的端口 16 位 接收方进程端口,用于交付到对应应用
长度 16 位 UDP 整个报文(首部+数据)的总长度,单位字节
校验和 16 位 检测报文传输差错,可选/强制取决于 IP 版本

3.3 UDP 的“不可靠”与应用层可靠性

你可能会感到疑惑:不可靠那还有什么用呢?

UDP 的「不可靠」不是缺点,而是主动放弃端到端的可靠性保障,换来极低的延迟和极高的灵活性。它把传输控制的主动权完全交给了应用层——如果业务需要可靠性,完全可以在 UDP 之上自行实现。

常见的应用层可靠传输方案:

  • 简单场景:停等协议,发一个等一个确认
  • 高效场景:自定义滑动窗口、超时重传、乱序重排
  • 工业级方案:QUIC 协议(HTTP/3 的底层),在 UDP 之上实现了完整的连接管理、可靠传输、流量控制、拥塞控制,同时解决了 TCP 的队头阻塞问题,建连更快、延迟更低

3.4 典型应用场景

UDP 适用于实时性优先、可容忍少量丢包的场景:

  • 域名解析:DNS
  • 实时音视频:直播、微信语音/视频、WebRTC
  • 实时游戏:MOBA、FPS 等低延迟要求的在线游戏
  • 网络管理:SNMP 简单网络管理协议
  • 地址分配:DHCP 动态主机配置协议
  • 新一代互联网协议:HTTP/3、QUIC

四、TCP 与 UDP 核心区别全景对比

对比维度 TCP UDP
连接特性 面向连接,需三次握手建立、四次挥手释放 无连接,无需建连与断连,随时发送
可靠性 可靠交付:不丢失、不重复、按序到达 不可靠:不保证送达、顺序与完整性
传输形式 面向字节流,无消息边界,存在粘包问题 面向数据报,保留消息边界,无粘包
流量控制 有,基于滑动窗口实现 无
拥塞控制 有,慢启动、拥塞避免、快重传、快恢复 无
首部开销 固定 20 字节,带选项最大 60 字节 固定 8 字节,开销极小
通信模式 仅支持一对一全双工通信 支持一对一、广播、组播
队头阻塞 存在,丢包会阻塞后续所有数据 不存在,数据包之间相互独立
传输效率 较低,连接管理与重传带来额外延迟 极高,几乎无额外开销,延迟极低
资源占用 较高,需要维护连接状态、缓冲区等 极低,几乎不需要额外资源
差错处理 出错自动重传,确保数据完整 出错直接丢弃,补救由应用层负责

五、常见问题

1. 为什么 TCP 是三次握手,不是两次或四次?

两次握手只能完成单向的序号同步,无法确认对方的接收能力;同时会导致历史失效的连接请求触发服务端建连,浪费资源。四次握手理论上可行,但三次已经能完成双向同步,四次属于冗余。

2. TIME_WAIT 为什么是 2MSL,不是 1MSL?

MSL 是报文在网络中的最长存活时间。2MSL 对应“一个 ACK 发过去 + 一个 FIN 重传回来”的最长时间,确保被动方没收到 ACK 时,能收到重传的 ACK 并正常关闭连接。

3. DNS 为什么默认用 UDP,不用 TCP?

DNS 查询通常只有几十字节,单次请求-响应即可完成,用 UDP 无需建连,延迟极低;如果查询结果超过 512 字节(或区域传输场景),DNS 也会自动切换到 TCP。

4. 什么是 TCP 队头阻塞?

TCP 是按字节顺序交付的,如果前面的某个报文丢了,后面所有到达的报文都要排队等重传,即使已经到了也不能提前交付给应用层,这就是队头阻塞。UDP 没有这个问题,QUIC 也通过流多路复用解决了这个问题。

最后

TCP 和 UDP,本质上是两种不同的工程取舍:

  • TCP 替你把所有脏活累活都干了,可靠、省心,但不够灵活,开销也大
  • UDP 把所有控制权都交给你,灵活、高效,但所有可靠性保障都要自己实现

没有哪个协议更优秀,只有哪个更适配你的业务场景。理解了它们的设计哲学,而不是死记硬背步骤,才能在真正做技术决策的时候,做出最合理的选择。