TCP/IP(五):传输层与 UDP——端口与无连接

写在前面

上一篇把网络层的 IP 地址、子网、路由和 ICMP 讲透了。但有个问题一直没回答:IP 只能把包送到某台主机,而一台机器上同时跑着浏览器、SSH、数据库几十个进程,一个进来的 IP 包到底该交给谁?

这个"交给谁"的问题,正是传输层要解决的。这一篇先讲传输层的职责和它的核心概念端口号,再展开两个传输层协议中较简单的那个——UDP :它为什么这么轻、靠什么校验、适合什么场景,并和 TCP 做一次选型对比。


一、传输层:从"主机到主机"到"进程到进程"

网络层(IP)解决的是主机到主机:路由器逐跳转发,把包送到目的 IP 那台机器。但它不关心机器内部。传输层在网络层之上,做两件 IP 做不了的事:

  • 进程到进程(端到端)的分发 —— 一台主机上多个进程共用一个 IP,传输层靠端口号区分,把数据准确交给目标进程。这个"多路分用(demultiplexing)“是传输层最根本的职责;
  • 端到端的可靠性与流量控制(可选) —— IP 是无连接、不可靠的,要不要重传、要不要按序、要不要限速,由传输层协议决定(TCP 提供,UDP 不提供)。
1
2
3
4
5
6
7
8
发送端进程                          接收端进程
   HTTP(80)                           HTTP(80)
   SSH(22)         ┌──── 传输层 ────┐    SSH(22)
   DNS(53) ──►     │  按端口号分发   │ ──► DNS(53)
                  └────────────────┘
                   网络层只管送到这台主机
                  (IP 不区分进程)

所以传输层是第一个真正的端到端层:数据从源进程的端口,直达目的进程的端口。理解了这一点,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 默认大致是 3276860999(cat /proc/sys/net/ipv4/ip_local_port_range 可查),Windows 默认是 4915265535。IANA 的建议标准是 49152~65535(RFC 6335)。

套接字(Socket)= IP + 端口

一个 套接字 用一个二元组唯一标识一条通信端点:

1
2
3
套接字 = (IP 地址,端口号)
例如:192.168.1.10:54321  →  本机某个客户端的临时端口
      93.184.216.34:443   →  某网站 HTTPS 服务

一条 TCP/UDP 连接(或数据流)由两端的套接字共同确定(源IP, 源端口, 目的IP, 目的端口, 协议),这叫五元组。抓包工具里看到的一条连接,就是靠五元组区分的。后面讲 TCP 会再回到这个概念。


三、UDP:无连接的极简协议

UDP(User Datagram Protocol,用户数据报协议)是传输层的"极简派”——RFC 768 一个文档就讲完了,核心特点四个字:尽力而为

  • 无连接 —— 发数据前不需要先建连,拿了 IP 和端口直接发,也不维护对端状态;
  • 不保证可靠 —— 不确认、不重传,包丢了就丢了,应用层自己处理;
  • 不保证顺序 —— 后发的可能先到,到达顺序不保证;
  • 没有流量 / 拥塞控制 —— 不会因为对端慢就降速,也不会因为网络拥塞就退让(所以 UDP 容易"冲",也因此在某些场景下比 TCP 快)。

UDP 的设计哲学是:传输层只做最少的搬运——加上端口、做个校验,剩下的全交给应用层 。这让它天然适合"宁可丢一点、也别等"的场景。


四、UDP 头部与校验和

UDP 的头部只有 8 字节,是 TCP 头部(最少 20 字节)的零头:

1
2
3
4
5
6
7
8
9
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          源端口 (16)          |         目的端口 (16)         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           长度 (16)           |          校验和 (16)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        数据(若有)...                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

四个字段各 2 字节:

  • 源端口 —— 可选,不用时填 0;
  • 目的端口 —— 必填,接收端靠它分发;
  • 长度 —— 整个 UDP 数据报(头部 + 数据)的字节数,最小值 8(只有头、没数据);
  • 校验和 —— 覆盖"伪首部 + UDP 头 + 数据"。

伪首部(pseudo header):连 IP 层一起校验

校验和最值得说的设计是伪首部:它不是真正传输的数据,只是计算校验和时临时拼在 UDP 头前面的 12 字节,内容来自 IP 头部:

1
2
3
4
5
6
7
8
9
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       源 IP 地址 (32)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      目的 IP 地址 (32)                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0 (8)     | 协议号 (8)=17 |       UDP 长度 (16)           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

为什么要把 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?一张表说清差异:

维度TCPUDP
连接方式面向连接(三次握手建连)无连接,拿来就发
可靠性可靠(确认 + 超时重传)不可靠,尽力而为
数据顺序按序交付,接收端无序感不保证顺序,可能乱序
流量控制有(滑动窗口)
拥塞控制有(慢启动、AIMD 等)无(应用层自行决定)
头部开销最少 20 字节固定 8 字节
传输效率 / 延迟有握手和确认开销,延迟略高极低开销,延迟低
通信模式一对一(点对点)一对一、一对多(广播/组播)
典型场景网页、文件传输、邮件、数据库DNS、DHCP、音视频、游戏、QUIC

一个简单的选型口诀:

  • 数据必须完整、不能丢 —— 选 TCP(文件传输、交易、数据库连接);
  • 实时性优先、容忍少量丢包 —— 选 UDP(音视频、游戏);
  • 查询极小、一问一答 —— 选 UDP(DNS);
  • 需要组播 / 广播 —— 只能 UDP(TCP 是严格的点对点);
  • 想要可靠又想摆脱 TCP 内核束缚 —— 在 UDP 上自建可靠层(QUIC)。

下一篇:TCP/IP(六):TCP 连接管理 —— TCP 怎么靠三次握手建立连接、四次挥手断开、为什么是"三次"不是"两次"、TIME_WAIT 状态存在的意义,把传输层里最经典也最常被追问的部分讲透。


参考:

Licensed under CC BY-NC-SA 4.0