Appearance
第三章:传输层 (Transport Layer)
目录
3.1 传输层服务
传输层的核心任务是:为运行在不同主机上的应用程序实体(进程)提供逻辑通信 (Logical Communication)。借助于逻辑通信,不同主机上的进程就如同直接相连一样。
1. 传输服务与端系统操作
传输协议主要是在端系统 (End Systems) 也就是边缘的主机上运行的,路由器等网络核心设备不处理传输层数据(它们通常只处理到底层网络层)。
- 发送者 (Sender):
- 接收应用层传递下来的消息 (App Message)。
- 确定报文段的首段字段值 (Header values)。
- 创建并分装成报文段 (Segment),最后把报文段传递给底层的网络层 (IP 层)。
- 接收者 (Receiver):
- 从网络层 (IP 层) 接收底层的推上来的报文段。
- 检查报文段的首部值,并完整提取出应用层消息。
- 通过对应的套接字 (Socket) 将该消息多路分解定点分发给指定的应用程序进程。
2. 传输层 vs. 网络层:一网打尽区别与联系
要理解传输层,必须搞清它与网络层 (Network Layer) 在服务范围上的根本区别:
- 网络层:提供主机 (Host) 之间的逻辑通信。
- 传输层:提供进程 (Process) 之间的逻辑通信。传输层重度依赖于网络层的主基建服务,并在其基础上做了大量增强。
形象的类比:安 (Ann) 与比尔 (Bill) 两家孩子的通信 想象安家的 12 个孩子需要给比尔家的 12 个孩子写信:
- 主机 (Host) = 房屋 (Houses)
- 进程 (Process) = 房屋里的孩子们 (Kids)
- 应用层消息 (App Message) = 信封里的信件文字 (Letters)
- 网络层协议 = 外面的邮政系统(保证把成堆的信从安家投递到了比尔家门口)。
- 传输层协议 = 家里的老大安和比尔(拿到邮递员送到家门口的一大捆信后,在家里进一步根据收件人名字,分发到具体对应的弟弟妹妹手里)。
3. 互联网两大主力传输协议
目前,互联网应用程序中提供两种截然不同的传输协议可供选择:
- 传输控制协议 (TCP: Transmission Control Protocol)
- 可靠、有序的传输交付:确保数据无差错、不丢失不乱序。
- 高级护航特性:提供了包括网络拥塞控制 (Congestion Control)、接收端流量控制 (Flow Control) 以及正式的数据传输前的连接建立 (Connection Setup) 机制。
- 用户数据报协议 (UDP: User Datagram Protocol)
- 不可靠、无序的交付:不保证数据送达和顺序。
- 本质是对底层 IP “尽力而为 (Best-effort)” 粗犷协议的一种无约束扩展。
⚠️ 传输层的盲区 (不提供的服务): 无论是功能庞大的 TCP,还是精简至极的 UDP,现有的互联网传输层协议都无法提供以下服务:
- 延迟保证 (Delay guarantees):不能保证数据在某此时限内到达。
- 带宽保证 (Bandwidth guarantees):不能保证你的应用在网络高峰期能独占多少速率。
3.2 多路复用和多路分解
多路复用(Multiplexing)与多路分解(Demultiplexing)是传输层提供进程间逻辑通信的核心机制。
1. 核心定义
- 发送端的多路复用(Multiplexing): 传输层从源主机的多个套接字(Socket)收集应用层数据,为每块数据封装上头部信息(主要是端口号)生成报文段,然后将这些报文段传递给网络层。
- 接收端的多路分解(Demultiplexing): 当传输层从网络层接收到报文段时,根据报文段头部的字段信息(端口号等),将数据定向交付到正确的套接字中。
2. 工作方式与段格式
主机接收到 IP 数据报时:
- 每个数据报都包含源 IP 地址和目的 IP 地址。
- 每个数据报中承载着一个传输层段,段中包含源端口号和目的端口号。
- 主机使用 IP 地址和端口号共同将报文段定向到适当的套接字。
段格式中的关键字段: 在 TCP/UDP 的报文段首部中,前 32 位被分配给:源端口号(16 bit) 和 目的端口号(16 bit)。
3. 无连接多路分解 (UDP)
- 套接字标识:UDP 套接字仅由一个二元组标识:
(目的 IP 地址, 目的端口号)。 - 分发规则:
- 接收主机收到 UDP 段后,检查段中的目的端口号。
- 将该段定向到具有该端口号的套接字。
- 特征:即使两个 UDP 报文段具有不同的源 IP 地址或源端口号,只要它们的目的 IP 和目的端口号相同,它们就会被定向到同一个接收套接字。
4. 面向连接的多路分解 (TCP)
- 套接字标识:TCP 套接字由一个四元组唯一标识:
- 源 IP 地址
- 源端口号
- 目的 IP 地址
- 目的端口号
- 分发规则:接收者使用所有这四个值来将报文段定向到对应的套接字。
- 特征:
- Web 服务器(如 Apache)可以同时支持多个 TCP 套接字。
- 即使所有进来的 TCP 段都有相同的目的 IP(服务器 IP)和相同的目的端口(如 80 端口),只要它们的源 IP 或源端口不同,它们就会被多路分解到不同的套接字中。
5. 总结
| 协议 | 多路分解依据 | 备注 |
|---|---|---|
| UDP | 目的端口号 | 相同目的地(IP+Port)的报文进同一个 Socket |
| TCP | 四元组 (源IP, 源Port, 目的IP, 目的Port) | 标识唯一的端到端连接 |
- 关键点:多路复用和多路分解机制存在于协议栈的所有层中。传输层是利用头部字段来实现进程间的分发。
6. 套接字 (Socket) 与 报文段 (Segment) 的深度关联
理解传输层,本质上是理解数据如何在“虚幻”的套接字与“实体”的报文段之间流转。
基本概念:
- 套接字 (Socket):是应用进程与传输层之间的“门”。在操作系统底层,它是一组数据的描述符。
- 报文段 (Segment):是传输层的 PDU,由“首部 (Header)” + “应用数据 (Payload)” 组成。其中首部包含的关键信息决定了它应该被塞进哪个“门”。
关系映射:UDP vs. TCP
- UDP(二元组映射):
- 内容:UDP 报文段非常简单,仅通过首部的“目的端口”寻找对应的 Socket。
- 关系:一个 UDP Socket 本质上只绑定了一个本地端口(如 12534)。任何目的地址为此端口的报文段都会无差别地进入这个 Socket。
- TCP(四元组映射):
- 内容:TCP 报文段首部包含复杂的连接信息。
- 关系:一个 TCP Socket 唯一对应一条特定的端到端虚连接。服务器可能在同一个 80 端口上挂载了 1000 个不同的 Socket,系统根据报文段中的(源 IP、源 Port、目的 IP、目的 Port)这四个值的组合,精准地投递到其中一个特定的 Socket 中。
- UDP(二元组映射):
3.3 无连接传输:UDP
UDP(User Datagram Protocol)被称为互联网传输协议的“基础版”或“简化版”。它本质上是在 IP 协议的基础上做了极少的扩展。
1. UDP 的核心特性
- 尽力而为(Best-effort)服务:
- UDP 报文段可能会在网络中丢失。
- UDP 报文段可能会**无序(Out-of-order)**交付给应用程序。
- 无连接性(Connectionless):
- 发送者和接收者之间没有握手过程。
- 每个 UDP 报文段的处理都是彼此独立的。
- 为什么需要 UDP?(UDP 的优势):
- 无需建立连接:减少了建立连接带来的 RTT 往返时延。
- 简单:发送者和接收者都不需要维护复杂的连接状态。
- 头部开销小:相比 TCP,UDP 的首部非常短小。
- 无拥塞控制:UDP 可以根据应用需求尽可能快地发送数据。即使在网络拥塞时,它也会继续尝试发送,这对于某些实时应用非常重要。
2. UDP 的使用场景
- 多媒体流媒体应用:对数据丢失有一定容忍度,但对传输速率和延迟高度敏感(如视频通话)。
- DNS(域名系统):查询过程简单,追求极速响应。
- SNMP(网络管理协议)。
- HTTP/3:为了解决传统 TCP 带来的队头阻塞等问题,QUIC(HTTP/3 的基础)选择建立在 UDP 之上。
- 可靠性补充:如果需要在 UDP 上实现可靠传输,必须在应用层添加所需的可靠性逻辑和拥塞控制(如 HTTP/3 所做的)。
3. UDP 报文段结构
UDP 首部只有 8 个字节,分为四个字段(每个字段占 16 位/2 字节):
| 字段 | 长度 | 描述 |
|---|---|---|
| 源端口号 | 16 bit | 发送进程的端口。 |
| 目的端口号 | 16 bit | 接收进程的目标端口。 |
| 长度 | 16 bit | 包含首部和数据的总字节数。 |
| 校验和 | 16 bit | 用于检测段在传输过程中是否出现了位翻转。 |
4. UDP 校验和 (Checksum)
目标:检测报文段中的错误(即传输过程中发生翻转的位)。
- 发送方动作:
- 将报文段内容(包括数据、首部以及部分 IP 伪首部)看作是一个 16 位整数序列。
- 对这些整数进行加法运算(使用反码求和算法:溢出的进位需要加回到结果的最低位,即“回绕”)。
- 最后将求和结果取反,存入校验和字段。
互联网校验和示例:加和两个 16 位整数
- 数据 1:
11100110 01100110- 数据 2:
11010101 01010101- 求和结果 (普通加法):
1 \ 10111011 10111011(第 17 位产生进位)- 回绕 (Wrap around): 将最高位的进位加到末尾 ->
10111011 10111100- 求反 (Checksum):
01000100 01000011(此值即为最终校验和字段)- 注意:如果数字改变(位翻转)但加和恰好没变,校验和仍会通过,这就是所谓的“弱保护”。
- 接收方动作:
- 对接收到的报文段执行同样的求和计算。
- 将计算出的结果与校验和字段中的值进行比对。
- 判定:
- 如果不相等:说明检测到错误。
- 如果相等:说明未检测到错误(但仍可能存在未被检测到的错位,即弱保护机制)。
5. 总结
UDP 虽然是“不可靠”的,但它的低延迟和低开销使其成为互联网基建中不可或缺的一部分。对于想要极致控制传输行为的应用,UDP 是在应用层自定义协议逻辑的最完美起点。
3.4 可靠数据传输原则
可靠数据传输(Reliable Data Transfer, rdt)是网络中最基本且复杂的问题之一。可靠服务的抽象屏蔽了底层不可靠信道的复杂性。
1. 可靠服务的实现挑战
- 抽象服务:应用层只需调用
rdt_send(),就认为数据能百分之百、按序到达目的地。 - 底层现状:网络层(如 IP)提供的服务是不可靠的(丢包、位错误、乱序)。
- rdt 协议的任务:通过控制逻辑屏蔽这些不可靠特征,向应用层呈现一个可靠的“虚拟信道”。
2. rdt 协议的逐步演进
rdt 1.0:完全可靠信道
- 假设:底层信道没有任何位错误,也不会丢包。
- 机制:发送方直接封装数据发送,接收方直接解封装接收。没有任何反馈。
rdt 2.0:可能有位错误的信道
- 新机制:
- 校验和 (Checksum):用于检测数据包是否在传输中被损坏。
- 确认 (ACK):接收方明确告知“收到了”。
- 反面确认 (NAK):接收方通知“出错了,请重发”。
- 控制策略:停止-等待 (Stop-and-Wait)。发送方发完一个包后必须停下来等待接收方的反馈。
rdt 2.1 & 2.2:处理确认消息的损坏
- 缺陷 (rdt 2.0):如果 ACK 或 NAK 消息在半路损坏了,发送方不知道接收方的真实状态,盲目重传会导致接收方收到重复包。
- rdt 2.1 引入序列号 (Sequence Number):
- 在每个包头添加序列号。
- 对于停止-等待协议,只需要 0 和 1 两个数字交替即可。
- 接收方可以通过序列号判断收到的包是“新包”还是“由于 ACK 丢失而重发的旧包”。
- rdt 2.2 (无 NAK 协议):去掉 NAK。接收方对上一个成功接收的包发送冗余 ACK。发送方收到两个相同的 ACK 时,视为 NAK 并重传。
rdt 3.0:底层信道可能丢包
- 新挑战:如果包丢了,接收方永远不会发 ACK,发送方会永远等待。
- 核心机制:定时器 (Timer):
- 发送方设定一个“合理”的超时时限。
- 如果超时未收到 ACK,发送方假定包已经丢失,直接重传。
- 序列号继续处理由于过早超时产生的重复包问题。
A. 回退 N 帧 (Go-Back-N, GBN)
- 窗口机制:允许最多发送
个未确认的包。 - 累积确认 (Cumulative ACK):ACK(
) 表示序号 的包都已经正确收到。 - 超时处理:如果包
超时,重传 及其之后的所有包。 - 接收方:极其简单,只按序接收。收到乱序包直接丢弃,不予缓存。
B. 选择性重传 (Selective Repeat, SR)
- 逐个确认:接收方对每个正确收到的包单独发送 ACK。
- 独立重传:发送方为每一个发出的包设置独立的定时器。
- 接收方缓存:可以接收并缓存乱序到达的包,直到凑齐顺序后统一交付给上层。
- 窗口限制:窗口大小必须
序列号范围的一半,以避免由于循环序列号导致的旧包/新包混淆。
总结:可靠性传输就是一套“博弈”机制,通过复杂的序列号、确认应答和定时重传,在不可靠的流沙上搭建起坚固的基石。