创见博客
TCP 和 UDP 有什么区别?从一次文件下载和视频通话说起
七崽爱吃小饼干2026/10/19阅读 0

同样跑在一条网络上,两个应用的表现可能完全不同。

下载文件时,少一个字节,压缩包就可能打不开;视频通话时,丢几帧画面,通常只是短暂花屏,通话还能继续。前者通常依赖 TCP,后者通常选择 UDP。

它们都在传输层,都建立在 IP 之上。区别不在于“谁更高级”,而在于它们对可靠性、延迟和顺序做了不同的取舍。

IP 只负责尽力而为

要理解 TCP 和 UDP,先看它们下面的 IP 协议。

IP 负责把数据包从一台机器送到另一台机器。它只承诺“尽力而为”:不保证送达,不保证顺序,也不保证不重复。网络拥塞、路由变化、链路故障,都可能让数据包丢失或乱序。

TCP 和 UDP 都复用 IP 的寻址能力,但在此之上做了不同的事:

  • TCP 补上可靠性、顺序和连接管理。
  • UDP 几乎不补,只增加端口、长度和校验和。

一个像挂号信,一个像明信片。

TCP:可靠的字节流

TCP 的全称是 Transmission Control Protocol,传输控制协议。它最核心的承诺是:把一段字节流可靠、有序地送到对端。

面向连接

TCP 通信前要先建立连接,也就是常说的三次握手:

text
客户端                         服务端
  | -------- SYN ---------->    |
  | <----- SYN + ACK -------    |
  | -------- ACK ---------->    |
  |         连接建立            |

握手的目的不只是“打个招呼”,而是确认双方都能收、都能发,并同步初始序列号。

通信结束后还要四次挥手,确保双方都把剩余数据发送完,再释放连接。

序列号、确认和重传

TCP 给每个字节编号。接收方收到数据后返回 ACK,告诉发送方“我已经收到了哪些字节”。如果发送方在一定时间内没收到确认,就会重传。

这带来几个结果:

  • 丢包会重传,所以数据最终能到达。
  • 接收方可以按序列号重新排序,所以应用读到的是有序字节流。
  • 重复包会被识别并丢弃,避免应用读到重复数据。

流量控制和拥塞控制

TCP 不只会重传,还会控制发送速度。

流量控制关注接收方:接收方缓冲区快满了,就通过窗口字段告诉发送方慢一点,别把数据压垮。

拥塞控制关注网络:发送方根据丢包和往返时延估计网络状况,动态调整发送速率。慢启动、拥塞避免、快速重传等机制,都是为了避免把网络打爆。

字节流,不是消息流

这是 TCP 最容易被误解的地方。

TCP 不保留应用层的消息边界。你调用两次 send 写入两条消息,接收方可能一次读到两条,也可能分三次读到一条。应用层必须自己定义边界,例如:

  • 固定长度
  • 长度前缀
  • 换行符或特殊分隔符
  • 更上层的协议格式,如 HTTP 头部的 Content-Length

所以 TCP 提供的是可靠有序的字节流,而不是“可靠的消息队列”。

代价:延迟和队头阻塞

可靠性不是免费的。

建立连接需要往返,重传需要等待,拥塞控制会限制发送速率。更麻烦的是队头阻塞:如果前面某个 TCP 段丢了,后面的数据即使已经到达,也不能立刻交给应用,必须等缺失部分重传并补齐。

在网页场景里,这会影响多个请求复用同一条连接时的表现。HTTP/2 的多路复用仍然跑在 TCP 上,所以底层丢包依然可能拖住所有流。

UDP:轻量的数据报

UDP 的全称是 User Datagram Protocol,用户数据报协议。它只做很少的事:

  • 复用端口,把数据交给正确的应用。
  • 保留消息边界,一个 UDP 数据报对应一次收发。
  • 提供校验和,尽量发现数据损坏。
  • 不建立连接,不确认,不重传,不排序,不控制拥塞。

UDP 头部只有 8 字节,而 TCP 头部最少 20 字节。这个差距在高频小包场景里很显眼。

无连接,发完就走

UDP 发送前不需要握手。应用把数据交给 UDP,UDP 加上端口和长度,交给 IP 发出去。

它不关心对方是否在线,也不关心数据有没有到达。丢包、乱序、重复,UDP 都不处理,交给应用自己决定。

这听起来很不可靠,但“不可靠”不等于“不能用”。很多实时场景宁愿丢一帧,也不愿意等重传。

支持广播和多播

UDP 支持一对多通信:

  • 广播:发给同一网络内的所有主机。
  • 多播:发给加入了特定组的主机。

DHCP、ARP 之外的很多发现协议、局域网服务发现、直播分发,都会利用这些能力。TCP 是点对点的,做不了这些事。

适合实时和低延迟场景

视频会议、在线游戏、语音通话、实时音视频,通常更在意“现在到”,而不是“一定到”。

一帧画面晚到 500 毫秒,基本就失去意义了。重传旧数据不如直接丢掉,继续处理下一帧。UDP 的低开销和无队头阻塞,正好适合这种场景。

DNS 也是典型例子。一次查询通常只有一问一答,建立 TCP 连接的成本可能比查询本身还高,所以 DNS 主要使用 UDP。响应太大或需要区域传送时,才会改用 TCP。

一张表看清差异

维度TCPUDP
连接方式面向连接,需要握手无连接,直接发送
可靠性确认、重传,保证送达不保证送达
顺序保证有序不保证顺序
消息边界不保留,是字节流保留数据报边界
头部大小最少 20 字节8 字节
流量控制有无
拥塞控制有无,应用自行处理
传输速度开销较大,通常更慢开销小,延迟低
广播/多播不支持支持
典型场景HTTP、文件传输、邮件、SSH、数据库DNS、DHCP、音视频、游戏、QUIC

怎么选:先问三个问题

实际开发中,可以用三个问题快速判断。

1. 丢一个包可以接受吗?

如果每一个字节都必须到达,例如转账记录、文件内容、配置下发,优先 TCP。

如果少量丢失可以接受,例如视频帧、游戏位置更新、实时语音,UDP 更合适。

2. 延迟和可靠性,哪个更重要?

需要严格可靠时,TCP 的重传和拥塞控制省去了大量应用层工作。

需要低延迟时,UDP 不会被重传和队头阻塞拖住。代价是应用要自己处理丢包、乱序和重复。

3. 是否需要广播、多播或极致轻量?

需要一对多、局域网发现、实时媒体分发时,UDP 往往是唯一选择。

如果只是普通请求响应,TCP 的可靠性更省心。HTTP/1.1 和 HTTP/2 都跑在 TCP 上,就是这个原因。

几个常见误解

误解一:UDP 一定比 TCP 快

UDP 头部更小、没有握手和重传,单看协议开销确实更轻。但“快”取决于场景。

如果网络丢包严重,应用在 UDP 之上自己实现可靠传输,最终可能比 TCP 更慢、更复杂。很多基于 UDP 的可靠协议,实际上是在用户态重新造了一套 TCP 式的机制。

误解二:TCP 保证消息完整

TCP 保证字节流可靠有序,但不保证应用层消息完整。一次 read 可能只读到半条消息,也可能读到一条半。应用必须自己处理粘包和拆包。

误解三:用了 UDP 就不能可靠

UDP 本身不提供可靠性,但应用可以在它之上实现确认、重传、排序和拥塞控制。QUIC 就是这么做的。

QUIC:把可靠性搬到 UDP 之上

HTTP/3 使用 QUIC,而 QUIC 基于 UDP。

它保留 UDP 的低开销和灵活调度,同时在用户态实现可靠传输、加密、多路复用和拥塞控制。因为每个流可以独立处理丢包,一个流受阻不会像 TCP 那样拖住所有流。

这也说明 TCP 和 UDP 不是简单的“可靠”与“不可靠”二选一。UDP 更像一块空白画布,协议设计者可以在上面画出自己需要的可靠性模型。

用 Node.js 感受一下

TCP 服务端:

js
const net = require('node:net');

const server = net.createServer((socket) => {
  socket.on('data', (data) => {
    socket.write(`echo: ${data}`);
  });
});

server.listen(3000);

UDP 服务端:

js
const dgram = require('node:dgram');

const server = dgram.createSocket('udp4');

server.on('message', (msg, rinfo) => {
  console.log(`from ${rinfo.address}:${rinfo.port}: ${msg}`);
});

server.bind(41234);

TCP 的 socket 是有状态的连接,可以持续读写;UDP 的 message 是一次独立数据报,发送方地址从 rinfo 里取。两种编程模型,对应两种协议假设。

最后

TCP 用握手、确认、重传、排序、流量控制和拥塞控制,换来可靠有序的字节流。它适合“一个字节都不能少”的场景,但也要接受延迟、开销和队头阻塞。

UDP 只提供最小的传输能力,把可靠性、顺序和拥塞控制交给应用。它适合实时、低延迟、一对多和自定义协议的场景,但应用必须自己兜底。

选协议时,不要问“哪个更先进”,先问:这个业务丢一个包会怎样?能等多久?是否需要多播?答案通常就在这三个问题里。

评论
0/100