第 2 章中,应用进程通过 socket 使用 TCP 或 UDP 的服务(见 Chapter 2.1 Application Layer 1 §2.1.2、§2.1.5)。本章打开 socket 下面的运输层,看这些服务怎样在端系统中实现。本篇介绍运输层的基本任务、复用与分用,以及最简单的运输协议 UDP;可靠传输原理见 Chapter 3.2 Transport Layer 2。
第 3 章分两条线展开:一条是运输层的一般原理,一条是 Internet 中的具体协议。原理与协议的对应关系如下:
| 原理 | 解决的问题 | 在 Internet 协议中的体现 |
|---|---|---|
| 复用与分用 | 一台主机上的多个进程如何共用网络 | UDP、TCP 都提供 |
| 可靠数据传输 | 下层会出错、丢包时,如何保证数据完整、有序 | TCP |
| 流量控制 | 发送方不要压垮接收方 | TCP |
| 拥塞控制 | 发送方不要压垮网络 | TCP |
3.1 运输层服务
第 1 章把运输层描述为“进程到进程的数据传输”(Chapter 1 Introduction §1.6.2)。这一节说明这句话的含义,以及它与网络层的分工。
3.1.1 进程之间的逻辑通信
两个应用进程可能相隔许多路由器和链路,但从应用看来,它们好像直接相连,只需把报文交给运输层,对方的运输层就会把它交给目标进程。
定义:逻辑通信(Logical Communication)
从某一层的视角看,通信双方好像直接连接在一起,只需关心本层的消息交换,不必关心下层经过哪些设备和链路。运输层为运行在不同主机上的应用进程提供逻辑通信。
运输协议只在端系统中运行:
- 发送方:把应用报文划分成若干运输层报文段(segment),加上运输层首部后交给网络层。
- 接收方:从网络层取得报文段,重组为报文,交给应用层。
- 路由器只处理到网络层(Chapter 1 Introduction §1.6.3),不检查运输层报文段的内容。
Internet 向应用提供不止一种运输协议,主要是 TCP 和 UDP。
3.1.2 运输层与网络层
运输层建立在网络层之上,两者的区别在于通信的端点不同:
| 层 | 提供的逻辑通信 | 端点 |
|---|---|---|
| 网络层 | 主机之间 | 主机(IP 地址) |
| 运输层 | 进程之间 | 进程(socket) |
例子:两户人家互相寄信
两栋房子里各有 12 个孩子,每个孩子都给对面房子的每个孩子写信。每栋房子由一个孩子(Ann、Bill)负责收集本家的信交给邮递员,并把邮递员送来的信分发给兄弟姐妹。
类比中的对象 网络中的对应物 孩子 进程 信封里的信 应用报文 房子 主机 Ann 和 Bill 运输层协议 邮政系统 网络层协议 Ann 和 Bill 只在自己家里工作,不参与邮件在邮局之间的运送,正如运输协议只在端系统中运行。若邮政系统不保证投递时间,Ann 和 Bill 也无法向孩子承诺信件几天内必到。
运输层依赖网络层的服务,也可以增强它:
- 网络层不能保证的时延和带宽,运输层也无法凭空提供。
- 网络层会丢失、损坏或乱序交付数据时,运输层可以通过重传、确认等机制,在端系统中提供可靠、有序的交付(§3.4 起展开)。
3.1.3 Internet 的运输层协议
Internet 的网络层协议 IP 只提供尽力而为(best-effort)的服务:尽量把数据报送到目的主机,但不保证送达、不保证顺序,也不保证数据完整。TCP 和 UDP 在此基础上提供不同程度的增强:
| 协议 | 交付服务 | 附加机制 |
|---|---|---|
| TCP | 可靠、按序交付 | 连接建立、流量控制、拥塞控制 |
| UDP | 不可靠、不保证顺序 | 只在 IP 之上增加进程间交付和差错检测,没有其他额外功能 |
两者都不提供时延保证和带宽保证。各类应用如何根据这些服务选择协议,见 Chapter 2.1 Application Layer 1 §2.1.5。
3.2 复用与分用
网络层只负责把数据送到主机,但主机上同时运行着浏览器、邮件客户端等多个进程,各自打开一个或多个 socket。运输层首先要解决的问题是:发送时如何让多个 socket 共用网络,接收时如何把报文段交给正确的 socket。
3.2.1 复用与分用的含义
定义:复用(Multiplexing)与分用(Demultiplexing)
- 复用:发送主机的运输层从多个 socket 收集数据,为每块数据加上首部(其中包含之后分用要用的信息),形成报文段交给网络层。
- 分用:接收主机的运输层根据报文段首部的信息,把收到的报文段交付给正确的 socket。
下图中,host 2 的运输层要把收到的报文段分别交给进程 P1 和 P2 的 socket(分用),同时把 P1、P2 发出的数据一起交给网络层(复用)。

3.2.2 分用依据:IP 地址与端口号
分用需要报文段携带“交给谁”的信息。主机收到 IP 数据报后,可以使用以下信息:
- 每个 IP 数据报有源 IP 地址和目的 IP 地址,并携带一个运输层报文段。
- 每个报文段的首部有源端口号和目的端口号,各占 16 bit。
TCP 和 UDP 报文段首部的前 32 bit 都是源端口号和目的端口号,其余首部字段两者不同,之后是应用数据。主机综合使用 IP 地址和端口号,把报文段导向相应的 socket。UDP 和 TCP 使用这些信息的方式不同,下面分别说明。
3.2.3 无连接分用(UDP)
UDP 不建立连接,接收方只看目的地址就能决定交给哪个 socket。
定义:UDP socket 的标识
UDP socket 由二元组标识:
主机收到 UDP 报文段后,检查其中的目的端口号,把报文段交给绑定该端口号的 socket。
因此,只要目的 IP 地址和目的端口号相同,即使源 IP 地址或源端口号不同,报文段也会交给同一个 socket。
源端口号仍然有用:它充当“回信地址”。接收方要回复时,把收到的源端口号作为回复报文段的目的端口号。
下图中,客户端 A 和 B 的进程都向服务器 C 的 6428 端口发送数据,两者到达服务器上的同一个 socket(P3);服务器回复时,分别以 9157 和 5775 作为目的端口号。

3.2.4 面向连接分用(TCP)
TCP 为每个连接维护单独的状态,所以接收方需要区分来自不同客户端的报文段,即使它们发往同一个端口。
定义:TCP socket 的标识
TCP socket 由四元组标识:
接收主机使用全部四个值,把报文段交给相应的 socket。
一台服务器主机可以同时维护许多 TCP socket,每个 socket 由自己的四元组标识。Web 服务器为每个连接的客户端使用不同的 socket;使用非持续 HTTP 时,每个请求都使用新的连接,因而对应不同的 socket(见 Chapter 2.1 Application Layer 1 §2.2.2)。
下图中,三个报文段的目的 IP 地址都是 C,目的端口号都是 80:
- A 的报文段与 B 的一个报文段源端口号都是 9157,但源 IP 地址不同(A 与 B)。
- B 的两个报文段源 IP 地址相同,但源端口号不同(9157 与 5775)。
三者的四元组互不相同,所以分别交给三个 socket。

socket 与进程不一定一一对应。多线程 Web 服务器只运行一个进程(P4),为每个连接创建一个新的 socket,交给不同的线程处理:

例题:同一目的端口,交给几个 socket?
服务器 C 的 80 端口先后收到三个报文段,源分别为 、、。
- 若它们是 UDP 报文段:分用只看目的地址 ,三者都交给同一个 socket。
- 若它们是 TCP 报文段:四元组各不相同,属于三条不同的连接,分别交给三个 socket。
UDP 服务器需要区分客户端时,只能由应用自己读取源地址;TCP 在运输层就把不同客户端的数据分开了。
3.3 无连接运输:UDP
有了分用,最简单的运输协议只需在 IP 之上做到“把数据交给正确的进程”,再加上基本的差错检测。UDP 就是这样的协议,定义于 RFC 768。
3.3.1 UDP 提供的服务
UDP 是“不加修饰”(no frills)的 Internet 运输协议,提供尽力而为的服务:
- 报文段可能丢失,也可能乱序交付给应用。
- 无连接:发送方和接收方之间没有握手,每个 UDP 报文段都独立处理,与其他报文段无关。
3.3.2 为什么需要 UDP
既然 TCP 提供可靠传输,仍然保留 UDP,是因为它“少做的事”对某些应用反而是优点:
| UDP 的特点 | 带来的好处 |
|---|---|
| 无需建立连接 | 省去建立连接带来的时延 |
| 不维护连接状态 | 发送方和接收方实现简单,服务器可以支持更多客户端 |
| 首部小 | 每个报文段的额外开销少 |
| 没有拥塞控制 | 应用可以按自己需要的速率发送,不被运输层压低 |
UDP 常用于流媒体等多媒体应用:这类应用能容忍少量丢失(loss tolerant),但对速率敏感(rate sensitive)。其他使用 UDP 的应用包括 DNS、SNMP(网络管理)和多人在线游戏。
应用需要可靠性但又想使用 UDP 时,可以在应用层自行加入确认、重传等机制(如 Chapter 2.2 Application Layer 2 §2.2.7 中的 QUIC)。
3.3.3 UDP 报文段格式
UDP 首部只有 4 个字段,每个 16 bit,共 8 byte:
| 字段 | 含义 |
|---|---|
| 源端口号 | 发送进程的端口,供对方回复时使用 |
| 目的端口号 | 用于在接收主机上分用 |
| 长度(length) | 整个 UDP 报文段的字节数,包括首部 |
| 校验和(checksum) | 用于检测报文段中的差错 |

3.3.4 UDP 校验和
数据在传输中可能发生比特翻转。UDP 不负责纠正错误,但提供校验和,让接收方能发现错误。
定义:Internet 校验和(Checksum)
发送方:
- 把报文段内容看作一串 16 bit 整数。
- 用反码求和(1’s complement sum)把它们相加:最高位产生的进位要回卷(wraparound),加到结果的最低位。
- 将和取反(每位 0、1 互换),得到校验和,填入 UDP 首部的校验和字段。
接收方:对收到的报文段重新计算,与校验和字段比较。
- 不相等:检测到差错。
- 相等:没有检测到差错,但仍可能存在未被发现的差错。
等价的检查方法是:接收方把所有 16 bit 字连同校验和字段一起做反码求和,结果全为 1 时没有检测到差错,出现 0 位就说明有差错。
例子:两个 16 bit 整数的校验和
1110 0110 0110 0110 + 1101 0101 0101 0101 --------------------- 1 1011 1011 1011 1011 回卷: 1011 1011 1011 1011 + 1 --------------------- 和: 1011 1011 1011 1100 校验和(取反):0100 0100 0100 0011第一次相加的和有 17 bit,最高位的进位 1 回卷到最低位,得到 16 bit 的和,取反后即为校验和。和与校验和相加得到
1111 1111 1111 1111。
校验和只能提供较弱的保护:若两个字在同一位置上的比特同时反向翻转,和保持不变,差错无法被发现。
例子:校验和检测不到的差错
沿用上例。若第一个字的最低位由 0 变为 1,同时第二个字的最低位由 1 变为 0:
原来:1110 0110 0110 0110 + 1101 0101 0101 0101 出错:1110 0110 0110 0111 + 1101 0101 0101 0100第一个字加 1、第二个字减 1,和不变,校验和也不变。接收方的检查通过,却收到了错误的数据。
链路层可能已经做了差错检测,但并非每段链路都检测,数据在路由器内存中也可能出错。运输层在端系统之间再检查一次,才能覆盖整条路径。