TCP协议与UDP协议
问题?
为什么刷网页、传文件时网络断一下就会加载失败,需要重连;而打游戏、看直播时,就算偶尔卡一下、丢几帧,画面却能很快跟上,不会整个停了。
这个就要看网络传输层的两大核心协议了: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 不保留消息边界,接收方无法区分原始数据的发送分段,可能出现“一次收到多条消息”或“一条消息分多次收到”的情况,这就是常说的粘包/拆包问题。
常见解决方案有三种:
- 固定消息长度:每条消息长度固定,接收方按固定长度读取
- 特殊分隔符:用换行符、特定字符作为消息边界
- 消息头+长度字段:头部指定消息体长度,接收方先读头再按长度读数据(最常用)
4. 流量控制(滑动窗口)
流量控制解决的是「发送方发太快,接收方处理不过来」的问题,核心是滑动窗口机制。
接收方在 TCP 头部的「窗口大小」字段中,实时告知发送方自己当前可用的接收缓冲区大小。发送方根据该值调整发送数据量,确保发送速率不超过接收方的处理能力。
- 当接收方缓冲区满时,会将窗口大小设为 0,发送方停止发送
- 发送方会定期发送零窗口探测报文,询问接收方是否有了新的缓冲区空间
5. 拥塞控制
拥塞控制解决的是「网络太堵,过多数据注入导致全网瘫痪」的问题,核心是动态调整发送方的拥塞窗口(cwnd),由四个经典算法组成:
- 慢启动:连接建立后,拥塞窗口从 1 个 MSS 开始,每经过一个 RTT 翻倍(每收到一个 ACK 增加 1 个 MSS),快速探测网络带宽
- 拥塞避免:当窗口达到慢启动阈值(ssthresh)后,改为线性增长(每个 RTT 增加 1 个 MSS),平稳提升速率,避免触发网络拥塞
- 快重传:收到 3 个重复的确认号时,立即重传丢失的报文,无需等待超时计时器到期
- 快恢复:触发快重传后,将慢启动阈值减半,拥塞窗口直接设为新阈值,进入拥塞避免阶段,避免速率骤降
2.2 连接管理机制深度讲解
三次握手(建立连接)
三次握手的核心目的:同步双方的初始序号、确认双方收发能力正常、避免失效连接浪费资源。
完整过程与状态变化:
- 客户端向服务端发送 SYN 同步报文,携带初始序号 seq=x,客户端进入
SYN_SENT状态 - 服务端收到后,回复 SYN+ACK 报文,携带自身初始序号 seq=y,确认号 ack=x+1,服务端进入
SYN_RCVD状态 - 客户端收到后,回复 ACK 确认报文,seq=x+1,ack=y+1,双方进入
ESTABLISHED状态,开始传输数据
为什么是三次,而不是两次?
- 为了同步双方的初始序列号:双方都需要确认对方收到了自己的初始序号,两次握手只能保证单向同步
- 防止已经失效的连接请求突然到达服务端,导致服务端错误建立连接、浪费资源
SYN 洪水攻击就是利用这个机制,攻击者伪造大量 SYN 报文,不回复第三次握手,导致服务端半连接队列耗尽,无法响应正常请求。防护方式有 SYN Cookie、调整半连接队列大小等。
四次挥手(断开连接)
TCP 是全双工通信,收发通道是独立的,需要分别关闭,因此需要四次交互。
完整过程与状态变化:
- 主动关闭方发送 FIN 结束报文,告知对方“我没有数据要发了”,进入
FIN_WAIT_1状态 - 被动关闭方回复 ACK 确认,进入
CLOSE_WAIT状态;主动方收到后进入FIN_WAIT_2状态,此时被动方仍可继续发送剩余数据 - 被动方数据发送完毕后,发送 FIN 结束报文,告知对方“我也发完了”,进入
LAST_ACK状态 - 主动方回复 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 把所有控制权都交给你,灵活、高效,但所有可靠性保障都要自己实现
没有哪个协议更优秀,只有哪个更适配你的业务场景。理解了它们的设计哲学,而不是死记硬背步骤,才能在真正做技术决策的时候,做出最合理的选择。



