写在前面
上一篇把网络层的 IP 地址、子网、路由和 ICMP 讲透了。但有个问题一直没回答:IP 只能把包送到某台主机,而一台机器上同时跑着浏览器、SSH、数据库几十个进程,一个进来的 IP 包到底该交给谁?
这个"交给谁"的问题,正是传输层要解决的。这一篇先讲传输层的职责和它的核心概念端口号,再展开两个传输层协议中较简单的那个——UDP :它为什么这么轻、靠什么校验、适合什么场景,并和 TCP 做一次选型对比。
一、传输层:从"主机到主机"到"进程到进程"
网络层(IP)解决的是主机到主机:路由器逐跳转发,把包送到目的 IP 那台机器。但它不关心机器内部。传输层在网络层之上,做两件 IP 做不了的事:
- 进程到进程(端到端)的分发 —— 一台主机上多个进程共用一个 IP,传输层靠端口号区分,把数据准确交给目标进程。这个"多路分用(demultiplexing)“是传输层最根本的职责;
- 端到端的可靠性与流量控制(可选) —— IP 是无连接、不可靠的,要不要重传、要不要按序、要不要限速,由传输层协议决定(TCP 提供,UDP 不提供)。
| |
所以传输层是第一个真正的端到端层:数据从源进程的端口,直达目的进程的端口。理解了这一点,TCP/UDP 的所有设计都围绕"如何更好地完成端到端通信"展开。
二、端口号与套接字
端口号是一个 16 位的整数,取值范围 0 ~ 65535。IANA 把它分成三段:
| 范围 | 名称 | 说明 | 例子 |
|---|---|---|---|
| 0 – 1023 | 知名端口(well-known) | 系统级服务常驻,Unix 下通常需要特权 | 22=SSH、53=DNS、80=HTTP、443=HTTPS |
| 1024 – 49151 | 注册端口(registered) | IANA 为应用登记 | 3306=MySQL、5432=PostgreSQL、6379=Redis |
| 49152 – 65535 | 动态 / 私有端口(dynamic) | 临时端口,客户端发起连接时由系统随机分配 | —— |
客户端发起请求时,操作系统从动态端口范围里临时挑一个作为源端口,去连服务端的知名端口。这个动态范围因系统而异:Linux 默认大致是 32768
60999(65535。IANA 的建议标准是 49152~65535(RFC 6335)。cat /proc/sys/net/ipv4/ip_local_port_range可查),Windows 默认是 49152
套接字(Socket)= IP + 端口
一个 套接字 用一个二元组唯一标识一条通信端点:
| |
一条 TCP/UDP 连接(或数据流)由两端的套接字共同确定:(源IP, 源端口, 目的IP, 目的端口, 协议),这叫五元组。抓包工具里看到的一条连接,就是靠五元组区分的。后面讲 TCP 会再回到这个概念。
三、UDP:无连接的极简协议
UDP(User Datagram Protocol,用户数据报协议)是传输层的"极简派”——RFC 768 一个文档就讲完了,核心特点四个字:尽力而为 。
- 无连接 —— 发数据前不需要先建连,拿了 IP 和端口直接发,也不维护对端状态;
- 不保证可靠 —— 不确认、不重传,包丢了就丢了,应用层自己处理;
- 不保证顺序 —— 后发的可能先到,到达顺序不保证;
- 没有流量 / 拥塞控制 —— 不会因为对端慢就降速,也不会因为网络拥塞就退让(所以 UDP 容易"冲",也因此在某些场景下比 TCP 快)。
UDP 的设计哲学是:传输层只做最少的搬运——加上端口、做个校验,剩下的全交给应用层 。这让它天然适合"宁可丢一点、也别等"的场景。
四、UDP 头部与校验和
UDP 的头部只有 8 字节,是 TCP 头部(最少 20 字节)的零头:
| |
四个字段各 2 字节:
- 源端口 —— 可选,不用时填 0;
- 目的端口 —— 必填,接收端靠它分发;
- 长度 —— 整个 UDP 数据报(头部 + 数据)的字节数,最小值 8(只有头、没数据);
- 校验和 —— 覆盖"伪首部 + UDP 头 + 数据"。
伪首部(pseudo header):连 IP 层一起校验
校验和最值得说的设计是伪首部:它不是真正传输的数据,只是计算校验和时临时拼在 UDP 头前面的 12 字节,内容来自 IP 头部:
| |
为什么要把 IP 地址也算进来?因为 UDP 想顺带验证:这个包确实发给了正确的 IP 和正确的协议(UDP 的 IP 协议号是 17) 。如果 IP 层把包投错了地址、或把别的协议误当成 UDP,校验和就对不上,UDP 会直接丢弃——等于给"端口分发"多上了一层保险。
一个 IPv4 的细节:UDP 校验和在 IPv4 里是可选的(填 0 表示“不校验”);IPv6 默认要求非零 UDP 校验和,因为 IPv6 没有头部校验和。RFC 6935 / RFC 6936 为特定、受控的隧道封装定义了零校验和例外,但普通 UDP 应用不应把例外当常规做法。实际工程中的通用 UDP 实现通常都会开启校验和。
五、UDP 适用场景
正因为"轻、快、不啰嗦",UDP 在这些场景里是更优解:
| 场景 | 为什么选 UDP |
|---|---|
| DNS | 查询报文极小(通常一个包搞定),一来一回就完事;若用 TCP,每次查询都三次握手,开销远大于数据本身 |
| DHCP | 客户端连 IP 都还没有,没法建连;靠 UDP 广播/组播完成地址发现,天然契合无连接 |
| 音视频通话、直播 | 容忍丢包(丢一帧花屏而已),但受不了 TCP 那种"丢包 → 重传 → 阻塞"带来的延迟和卡顿 |
| 在线游戏 | 状态实时刷新,旧数据重传毫无价值,宁可丢也要保住最新帧 |
| QUIC / HTTP/3 | 在 UDP 之上自行实现可靠性、加密、多路复用和拥塞控制,绕开 TCP 内核栈的演进包袱 |
最后一条尤其重要:QUIC 选择"在 UDP 上重建一套类 TCP 的可靠传输" ,而不是直接用 TCP,是因为 TCP 的连接管理、拥塞控制都焊死在操作系统内核和中间网络设备里,升级一次要等十年;而 UDP 是个"白板",把控制逻辑放回用户态就能快速迭代——这是现代协议设计的一个重要趋势。
六、TCP vs UDP 选型对比
回到工程实践:到底该选 TCP 还是 UDP?一张表说清差异:
| 维度 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(三次握手建连) | 无连接,拿来就发 |
| 可靠性 | 可靠(确认 + 超时重传) | 不可靠,尽力而为 |
| 数据顺序 | 按序交付,接收端无序感 | 不保证顺序,可能乱序 |
| 流量控制 | 有(滑动窗口) | 无 |
| 拥塞控制 | 有(慢启动、AIMD 等) | 无(应用层自行决定) |
| 头部开销 | 最少 20 字节 | 固定 8 字节 |
| 传输效率 / 延迟 | 有握手和确认开销,延迟略高 | 极低开销,延迟低 |
| 通信模式 | 一对一(点对点) | 一对一、一对多(广播/组播) |
| 典型场景 | 网页、文件传输、邮件、数据库 | DNS、DHCP、音视频、游戏、QUIC |
一个简单的选型口诀:
- 数据必须完整、不能丢 —— 选 TCP(文件传输、交易、数据库连接);
- 实时性优先、容忍少量丢包 —— 选 UDP(音视频、游戏);
- 查询极小、一问一答 —— 选 UDP(DNS);
- 需要组播 / 广播 —— 只能 UDP(TCP 是严格的点对点);
- 想要可靠又想摆脱 TCP 内核束缚 —— 在 UDP 上自建可靠层(QUIC)。
下一篇:TCP/IP(六):TCP 连接管理 —— TCP 怎么靠三次握手建立连接、四次挥手断开、为什么是"三次"不是"两次"、TIME_WAIT 状态存在的意义,把传输层里最经典也最常被追问的部分讲透。
参考: