1. 完整回答
TCP 和 UDP 都是传输层协议,主要负责在不同主机的应用进程之间传输数据。两者最核心的区别在于连接方式、可靠性以及数据传输形式。
TCP 是面向连接的可靠传输协议,提供有序的字节流服务。通信前通常需要经过三次握手建立连接,双方同步初始序列号。传输过程中,TCP 通过序列号、确认应答和超时重传保证数据可靠、有序地交付,同时利用滑动窗口提高传输效率,通过流量控制和拥塞控制调整发送速度,避免接收方处理不过来或者网络发生拥塞。
UDP 是无连接的数据报协议,发送前不需要建立传输层连接,直接把数据报交给网络层发送。UDP 本身不保证数据一定到达,也不保证顺序,并且没有自动重传和拥塞控制机制。它的首部只有 8 字节,协议开销较小,能够保留消息边界,适合对实时性要求较高的场景。
在实际应用中,TCP 更适合文件传输、数据库连接等要求数据完整、有序的业务。UDP 常用于实时音视频、在线游戏和 DNS 查询,因为这些场景往往更关注时延,部分数据丢失也可以由应用容忍或自行处理。
另外,UDP 也可以实现可靠传输,例如 HTTP/3 使用的 QUIC 就基于 UDP,在上层实现了可靠性、流量控制和拥塞控制。
所以选择 TCP 还是 UDP,主要取决于业务对可靠性、实时性、消息边界和传输开销的要求。
2. 核心原理与关键概念
2.1 核心原理
TCP 的核心流程:
建立连接 → 可靠传输 → 流量与拥塞控制 → 关闭连接
-
建立连接:通过 SYN → SYN+ACK → ACK 三次握手,同步双方的初始序列号,建立连接状态。
-
可靠传输:发送端给数据字节编号,接收端通过 ACK 确认已收到的数据。发生丢包时,通过超时重传或快速重传进行恢复,并将数据按序交付应用。
-
传输控制:滑动窗口允许多个数据段连续发送;流量控制依据接收窗口限制发送量;拥塞控制根据网络状态调整发送速率。
-
关闭连接:通常通过 FIN → ACK → FIN → ACK 完成双向关闭,双方分别结束自己的发送方向。
UDP 的核心流程:
应用发送数据 → 封装 UDP 数据报 → IP 层传输 → 接收端校验并交付应用
-
发送端将应用数据加上 UDP 首部,主要包含源端口、目标端口、长度和校验和。
-
UDP 将数据报交给 IP 层发送,无需事先建立传输层连接。
-
接收端收到数据报后,进行必要的校验,并根据目标端口交付对应应用。
-
如果发生丢包、重复或乱序,UDP 本身不会自动恢复,需要应用层根据业务需求处理。
2.2 关键概念
| 概念 | 作用 |
|---|---|
| 序列号(Sequence Number) | TCP 对字节进行编号,用于识别丢失、重复及乱序数据 |
| 确认应答(ACK) | 告诉发送方已成功接收的数据范围,TCP 通常采用累计确认 |
| 滑动窗口 | 允许多个数据段在未收到确认时继续发送,提高链路利用率 |
| 接收窗口(rwnd) | 反映接收方当前能够接收的数据量 |
| 拥塞窗口(cwnd) | 限制发送方在网络中尚未确认的数据量 |
| MTU | 最大传输单元,限制某条链路上单个网络层数据包的大小 |
| MSS | TCP 最大报文段长度,限制单个 TCP 报文段的数据部分大小 |
TCP 实际允许发送的未确认数据量,同时受到 rwnd 和 cwnd 的约束,通常不能超过两者中的较小值。
3. 高频面试追问
3.1 TCP 为什么需要三次握手?两次可以吗?
三次握手主要是为了同步双方的初始序列号,确认双方能够建立通信,并减少历史重复连接请求带来的问题。
具体过程是:
-
第一次握手:客户端发送 SYN,携带自己的初始序列号,请求建立连接。
-
第二次握手:服务端返回 SYN+ACK,确认客户端的 SYN,同时发送自己的初始序列号。
-
第三次握手:客户端发送 ACK,确认服务端的 SYN,服务端收到后进入已建立连接状态。
如果只有两次握手,服务端发送 SYN+ACK 后,就无法确认客户端是否收到了自己的初始序列号。
例如,一个历史上延迟的 SYN 报文突然到达服务端,如果服务端直接建立连接,就可能维护一个客户端实际上没有准备使用的连接。
三次握手通过最后一次确认降低了这种风险,让双方建立正确的连接状态。
3.2 TCP 是如何保证可靠传输的?
TCP 主要通过序列号、确认应答、重传机制和按序交付实现可靠传输。
首先,TCP 给发送的数据字节编号,接收方通过 ACK 告诉发送方已经连续收到了哪些数据。
如果发送方长时间没有收到确认,就会触发超时重传。另外,接收方发现数据出现缺口时,可以发送重复 ACK,发送方根据这些信息进行快速重传,不必一直等待超时。
如果数据乱序到达,接收方可以先缓存后续数据,等缺失的数据补齐后,再按序交付给应用。
如果数据重复到达,也可以通过序列号识别,避免重复交付。
不过,TCP 的可靠性有边界。如果连接最终失败,数据仍可能无法成功传到对端,应用需要处理连接错误。
继续追问:TCP 已经可靠,为什么业务还需要确认和重试?
因为 TCP 的 ACK 只能反映对端 TCP 协议栈接收数据的情况,不能证明业务已经处理成功。
例如,服务端收到了支付请求,但数据库事务执行失败,TCP 本身无法解决这个问题。因此关键业务通常还需要应用层响应、超时重试以及幂等处理。
3.3 TCP 的滑动窗口、流量控制和拥塞控制是什么关系?
滑动窗口是提高传输效率的基础机制,流量控制保护接收方,拥塞控制保护网络。
如果每发送一个数据段,都必须等待 ACK 后才能发送下一个数据段,网络利用率就会比较低。
滑动窗口允许发送方连续发送多个数据段。随着 ACK 返回,已经确认的数据移出窗口,发送窗口向前移动。
流量控制主要依赖接收方通告的 rwnd。如果接收方处理数据比较慢,缓冲区剩余空间减少,就会通知发送方缩小发送窗口,必要时可以通告零窗口。
拥塞控制主要依赖 cwnd。发送方通过网络反馈判断拥塞情况,动态调整允许在途的数据量。
经典 TCP 拥塞控制包含慢启动、拥塞避免、快速重传和快速恢复等机制。
比如慢启动阶段,拥塞窗口通常随每轮往返确认快速增长;进入拥塞避免后,增长变得相对平缓。检测到拥塞后,发送方会调整发送速率。
最终,发送端同时受到接收窗口和拥塞窗口的约束。
3.4 TCP 为什么会出现粘包和拆包?怎么解决?
根本原因是 TCP 面向字节流,不保留应用层消息边界。
假设客户端连续发送两条业务消息:
-
第一条:
Hello -
第二条:
World
服务端一次读取可能得到 HelloWorld,也可能分多次读取,例如先得到 Hel,再得到 loWorld。
这是因为 TCP 只保证字节按序交付,发送时的写入次数与接收时的读取次数没有一一对应关系。
解决方法是在应用层定义明确的消息边界,常见有三种:
-
固定长度:每条消息长度固定。
-
分隔符:使用换行符等特殊标记区分消息。
-
长度字段:消息头携带消息体长度,接收方按长度读取完整消息。
实际项目里,我通常更倾向于消息长度 + 消息体的方式,适合长度不固定的二进制协议,同时会限制最大消息长度,避免异常长度导致内存问题。
继续追问:UDP 会出现粘包吗?
UDP 保留数据报边界,接收端一次读取通常对应一个数据报,不会把多个数据报合并成一条消息。但如果接收缓冲区太小,数据报可能被截断,剩余部分通常会被丢弃。
3.5 TCP 为什么通常四次挥手?TIME_WAIT 有什么作用?
因为 TCP 是全双工连接,两个方向的数据发送需要分别关闭。
假设客户端主动关闭连接:
-
客户端发送 FIN,表示自己不再发送数据。
-
服务端返回 ACK,确认收到 FIN。
-
服务端处理完剩余数据后,也发送 FIN。
-
客户端返回 ACK,完成双方的关闭确认。
之所以通常是四次,是因为服务端收到 FIN 时,可能还有数据需要发送,所以 ACK 和 FIN 往往分开发送。
如果服务端已经没有数据需要发送,ACK 和 FIN 也可以合并,实际可能只看到三个报文段。
客户端主动关闭后,通常会进入 TIME_WAIT 状态。
TIME_WAIT 主要有两个作用:
-
保证关闭过程的可靠性:如果最后一个 ACK 丢失,对端会重传 FIN,本端仍然能够重新回复 ACK。
-
避免历史报文干扰新连接:等待旧连接中可能残留的报文逐渐消失。
按照 TCP 协议规范,TIME_WAIT 的等待时间通常描述为 2MSL,即两倍最大报文生存时间,具体实现和优化会因操作系统而异。
3.6 UDP 一定比 TCP 快吗?为什么不能无限制地发送 UDP 数据?
UDP 的协议开销更小,但实际传输速度和延迟并不一定优于 TCP。
UDP 不需要 TCP 的三次握手,也没有内置的确认重传和有序交付机制,所以对于短消息、实时数据,通常可以减少等待和控制开销。
但是传输速度还受到带宽、丢包率、网络拥塞、应用实现等因素影响。
TCP 具有成熟的拥塞控制和丢包恢复机制,在需要完整传输大量数据时,往往能够取得更稳定的有效吞吐量。
而 UDP 自身没有拥塞控制,如果应用持续高速发送,超过网络处理能力,就可能导致路由器队列积压、大量丢包,最终降低实际传输效率。
所以使用 UDP 时,应用或上层传输协议也应进行限速、拥塞控制,并根据网络情况调整发送速率。
3.7 UDP 可以实现可靠传输吗?QUIC 为什么基于 UDP?
UDP 可以通过上层协议实现可靠传输,QUIC 就是典型例子。
如果要基于 UDP 实现可靠传输,可以增加序列号、ACK、超时重传、乱序缓存和去重机制。
如果还需要较好的传输性能和网络公平性,就要加入滑动窗口、流量控制以及拥塞控制。
QUIC 基于 UDP 实现了这些能力,同时集成 TLS 1.3 安全通信机制,并支持多路复用。
QUIC 的一个重要优势是多个数据流可以相对独立地传输。
例如,传统 HTTP/2 使用单个 TCP 连接承载多个请求。如果 TCP 中某个数据段丢失,后续字节即使已经到达,也要等待缺失的数据恢复,可能影响其他请求。
QUIC 在不同流之间提供独立的有序交付,一个流发生数据丢失时,通常不会因为传输层的顺序要求阻塞其他流。
因此 HTTP/3 选择 QUIC,可以改善连接建立效率和多路复用场景下的队头阻塞问题。
3.8 UDP 数据报太大会发生什么?MTU 和 MSS 有什么关系?
UDP 数据报过大时,可能引发 IP 分片或者直接丢包,影响传输可靠性和效率。
MTU 表示链路允许承载的最大网络层数据包大小。
例如,以太网常见 MTU 为 1500 字节。在不考虑额外选项的 IPv4 TCP 场景中,TCP 的 MSS 通常可以设置为:
1500 - 20(IP首部)- 20(TCP首部)= 1460 字节
TCP 可以依据 MSS 和路径 MTU 将字节流分段,尽量避免 IP 分片。
UDP 则需要应用或上层协议控制每个数据报的大小。
如果 UDP 数据报超过路径 MTU,IPv4 可能发生分片,也可能因禁止分片而无法发送;IPv6 路由器不会执行分片,可能直接丢弃过大的数据包。
分片会降低可靠性,因为只要一个分片丢失,整个原始 IP 数据包就可能无法重组。
所以在实际项目中,我会尽量控制 UDP 数据报大小,结合路径 MTU 发现机制避免分片。
3.9 实际项目中如何选择 TCP 和 UDP?
我主要根据数据完整性、时延要求以及能否容忍丢包来选择。
如果是订单、支付请求、数据库通信或者文件传输,数据必须完整、有序,通常会选择 TCP,或者使用 QUIC 提供的可靠流。
如果是实时语音、视频会议或者在线游戏中的位置同步,通常会优先考虑 UDP。
以在线游戏为例,玩家的位置可能每隔几十毫秒更新一次。如果一条旧位置消息丢失,直接使用最新位置往往更合理,没必要为了补齐旧数据增加等待时间。
但游戏中的购买道具、结算结果等关键操作仍然需要可靠交付,可以使用 TCP、QUIC 可靠流,或者基于 UDP 实现应用层可靠消息。
因此,同一个系统也可能同时使用不同的传输方案,为不同业务选择合适的可靠性和时延策略。
3.10 项目中出现 TCP 重传或者 UDP 丢包,怎么排查?
我一般会先区分是网络传输问题、接收端处理问题,还是应用自身的问题。
对于 TCP,我会关注 RTT、重传数量、接收窗口、拥塞情况和连接状态。
如果大量重传,需要结合抓包判断是否存在数据丢失、确认包丢失、网络拥塞或异常延迟。
如果接收窗口长期很小,还需要检查应用是否读取数据过慢,导致接收缓冲区被占满。
对于 UDP,我会在应用层增加序列号,统计丢包和乱序情况,同时检查发送速率、数据报大小、Socket 接收缓冲区以及服务端处理能力。
排查时可以结合 tcpdump、Wireshark 和系统网络统计信息,对比发送端与接收端的数据。
最终会根据原因处理,例如控制发送速率、避免 IP 分片、优化接收线程,或者调整缓冲区配置。
4. 易混淆概念对比
4.1 TCP 字节流与 UDP 数据报
| 对比项 | TCP 字节流 | UDP 数据报 |
|---|---|---|
| 数据组织方式 | 连续字节序列 | 独立数据报 |
| 消息边界 | 不保留应用消息边界 | 保留数据报边界 |
| 接收顺序 | 按序交付字节 | 不保证按发送顺序到达 |
| 丢包处理 | 协议内部进行检测和恢复 | 需要应用层自行处理 |
| 应用层处理 | 需要设计消息拆分规则 | 需要处理丢包、乱序、截断等情况 |
4.2 流量控制与拥塞控制
| 对比项 | 流量控制 | 拥塞控制 |
|---|---|---|
| 解决的问题 | 接收方处理不过来 | 网络承载能力不足 |
| 主要依据 | 接收方缓冲区状态 | RTT、丢包、显式拥塞反馈等网络信号 |
| 核心机制 | 接收窗口 rwnd | 拥塞窗口 cwnd |
| 控制目标 | 防止接收端缓冲区溢出 | 避免网络过载,合理利用带宽 |
两者同时影响 TCP 的发送量,流量控制关注接收端,拥塞控制关注网络。
4.3 TCP 分段与 IP 分片
| 对比项 | TCP 分段 | IP 分片 |
|---|---|---|
| 所属层次 | 传输层 | 网络层 |
| 主要原因 | 将字节流组织成适当大小的 TCP 报文段 | IP 数据包超过可传输的大小 |
| 关键参数 | MSS | MTU |
| 重组方式 | TCP 按序号恢复字节流 | IP 层重组分片 |
| 主要影响 | 属于正常传输机制 | 会增加开销和丢包风险 |
TCP 通常通过合理分段避免 IP 分片,而 UDP 应用也应该根据路径 MTU 控制数据报大小。
4.4 TCP、UDP 与 QUIC
| 对比项 | TCP | UDP | QUIC |
|---|---|---|---|
| 建立连接 | 三次握手 | 无传输层握手 | 有连接建立过程 |
| 可靠传输 | 支持 | 原生不支持 | 支持可靠流 |
| 拥塞控制 | 支持 | 原生不支持 | 支持 |
| 数据形式 | 有序字节流 | 数据报 | 多路复用的流,也支持数据报扩展 |
| 安全性 | 本身不提供加密 | 本身不提供加密 | 集成 TLS 1.3 |
| 典型应用 | HTTP/1.1、HTTP/2、数据库 | DNS、实时音视频 | HTTP/3 |
QUIC 基于 UDP 构建,但具备连接管理、可靠传输、流量控制和拥塞控制能力,因此不能直接把 QUIC 的特性等同于原生 UDP。
5. 总结
TCP 和 UDP 都属于传输层协议,核心区别是传输保障和数据组织方式。TCP 面向连接,提供可靠、有序的字节流,通过三次握手建立连接,利用序列号、ACK 和重传机制恢复丢失的数据,并通过流量控制和拥塞控制调整发送速度,适合文件传输、数据库通信等场景。UDP 无连接,按数据报传输,协议开销小、保留消息边界,但不保证可靠性和顺序,更适合实时音视频、游戏状态同步等场景。UDP 也可以在上层实现可靠传输,例如 QUIC。实际选择时,我会结合可靠性、时延和业务需求进行权衡,同时注意 TCP 的可靠交付不等于业务执行成功,UDP 的低协议开销也不代表一定具有更低的实际延迟。