3. Functional Specification - Part 1 (功能规范 - 第1部分)
本部分包含TCP的核心技术规范:头部格式、术语和序列号机制。
3.1. Header Format (头部格式)
TCP段 (TCP segments) 作为互联网数据报 (internet datagrams) 发送。互联网协议头部携带多个信息字段,包括源和目标主机地址 [2]。TCP头部跟随互联网头部,提供TCP协议特有的信息。这种划分允许存在除TCP之外的主机级协议。
TCP头部格式
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
TCP头部格式
注意: 一个刻度标记代表一个比特位置
字段详细说明
Source Port (源端口): 16位
源端口号。
用途: 标识发送方主机上的进程。
Destination Port (目标端口): 16位
目标端口号。
用途: 标识接收方主机上的进程。
Sequence Number (序列号): 32位
此段中第一个数据八位字节的序列号 (除非存在SYN)。如果存在SYN,则序列号是初始序列号 (Initial Sequence Number, ISN),第一个数据八位字节是ISN+1。
关键点:
- 每个数据字节都有唯一的序列号
- SYN段使用ISN,数据从ISN+1开始
- 序列号空间: 0 到 2³² - 1
Acknowledgment Number (确认号): 32位
如果设置了ACK控制位,此字段包含段的发送者期望接收的下一个序列号的值。一旦建立连接,此字段始终发送。
累积确认机制:
- 确认号为X表示所有小于X的字节已被接收
- 不包括X本身
Data Offset (数据偏移): 4位
TCP头部中的32位字数。这指示数据从哪里开始。TCP头部 (即使包括选项) 是32位的整数倍长。
计算公式:
头部长度 (字节) = Data Offset × 4
最小值: 5 (20字节)
最大值: 15 (60字节)
Reserved (保留): 6位
保留供将来使用。必须 (MUST) 为零。
Control Bits (控制位): 6位 (从左到右)
| 标志 | 全称 | 含义 |
|---|---|---|
| URG | Urgent | 紧急指针字段有效 |
| ACK | Acknowledgment | 确认字段有效 |
| PSH | Push | Push功能 |
| RST | Reset | 重置连接 |
| SYN | Synchronize | 同步序列号 |
| FIN | Finish | 发送方没有更多数据 |
标志组合含义:
SYN = 1: 连接建立请求
SYN + ACK = 1: 连接建立响应
FIN = 1: 连接终止请求
RST = 1: 异常终止连接
PSH = 1: 立即推送数据到应用层
URG = 1: 存在紧急数据
Window (窗口): 16位
从确认字段中指示的八位字节开始,此段的发送者愿意接受的数据八位字节数。
流量控制:
- 窗口大小 = 0: 停止发送数据
- 窗口大小 > 0: 可发送窗口大小字节的数据
- 最大窗口: 65,535字节 (可通过窗口缩放选项扩展)
示例:
ACK = 1000, Window = 5000
→ 可以接收序列号 1000-4999 的数据 (5000字节)
Checksum (校验和): 16位
校验和字段是头部和文本中所有16位字的补码和的16位补码。如果段包含奇数个要校验和的头部和文本八位字节,则最后一个八位字节在右侧填充零,以形成用于校验和目的的16位字。填充不作为段的一部分传输。在计算校验和时,校验和字段本身被替换为零。
伪头部 (Pseudo Header):
校验和还覆盖概念上前缀到TCP头部的96位伪头部。此伪头部包含源地址、目标地址、协议和TCP长度。这为TCP提供了对错误路由段的保护。
+--------+--------+--------+--------+
| Source Address |
+--------+--------+--------+--------+
| Destination Address |
+--------+--------+--------+--------+
| zero | PTCL | TCP Length |
+--------+--------+--------+--------+
PTCL = 6 (TCP协议号)
TCP Length = TCP头部长度 + 数据长度 (以八位字节为单位)
校验和计算步骤:
def calculate_tcp_checksum(pseudo_header, tcp_header, data):
# 1. 将校验和字段设为0
# 2. 组合伪头部、TCP头部和数据
# 3. 按16位字累加
# 4. 将进位加到低16位
# 5. 取补码
pass
Urgent Pointer (紧急指针): 16位
此字段传达紧急指针的当前值,作为此段中序列号的正偏移量。紧急指针指向紧急数据后面的八位字节的序列号。此字段仅在设置了URG控制位的段中解释。
使用场景:
- Ctrl+C 中断信号
- Telnet中断命令
- 需要优先处理的控制信息
示例:
SEG.SEQ = 1000
URG Pointer = 10
→ 紧急数据结束于序列号 1010
→ 序列号 1000-1009 是紧急数据
Options (选项): 可变
选项可以占用TCP头部末尾的空间,长度为8位的倍数。所有选项都包含在校验和中。选项可以从任何八位字节边界开始。选项格式有两种情况:
情况1: 单个八位字节的option-kind 情况2: 一个八位字节的option-kind,一个八位字节的option-length,以及实际的option-data八位字节
option-length计算option-kind和option-length两个八位字节以及option-data八位字节。
重要: TCP必须 (MUST) 实现所有选项。
当前定义的选项
| Kind (八进制) | Length | 含义 |
|---|---|---|
| 0 | - | 选项列表结束 (End of Option List) |
| 1 | - | 无操作 (No-Operation) |
| 2 | 4 | 最大段大小 (Maximum Segment Size) |
选项详细说明
1. End of Option List (选项列表结束)
+--------+
|00000000|
+--------+
Kind=0
- 此选项代码指示选项列表的结束
- 这可能不与根据Data Offset字段的TCP头部的结束重合
- 用于所有选项的结束,而不是每个选项的结束
- 仅在选项的结束不会与TCP头部的结束重合时需要使用
2. No-Operation (无操作)
+--------+
|00000001|
+--------+
Kind=1
- 此选项代码可以在选项之间使用
- 例如,将后续选项的开头对齐到字边界
- 不保证发送方将使用此选项
- 接收方必须准备好处理不从字边界开始的选项
3. Maximum Segment Size (最大段大小)
+--------+--------+---------+--------+
|00000010|00000100| max seg size |
+--------+--------+---------+--------+
Kind=2 Length=4
Maximum Segment Size Option Data: 16位
- 如果存在此选项,则它传达发送此段的TCP的最大接收段大小
- 此字段必须仅在初始连接请求中发送 (即,在设置了SYN控制位的段中)
- 如果不使用此选项,则允许任何段大小
MSS说明:
- 默认MSS = 536字节 (互联网默认)
- 以太网常用MSS = 1460字节 (1500 - 20 IP头 - 20 TCP头)
- MSS仅指数据部分,不包括TCP/IP头部
Padding (填充): 可变
TCP头部填充用于确保TCP头部在32位边界上结束,数据在32位边界上开始。填充由零组成。
3.2. Terminology (术语)
在我们讨论TCP操作之前,需要引入一些详细的术语。TCP连接的维护需要记住几个变量。我们设想这些变量存储在称为传输控制块 (Transmission Control Block, TCB) 的连接记录中。
TCB中存储的变量
TCB中存储的变量包括:
- 本地和远程套接字号
- 连接的安全性和优先级
- 指向用户发送和接收缓冲区的指针
- 指向重传队列和当前段的指针
- 与发送和接收序列号相关的几个变量
发送序列变量 (Send Sequence Variables)
| 变量 | 全称 | 说明 |
|---|---|---|
| SND.UNA | Send Unacknowledged | 发送未确认 (最老的未确认序列号) |
| SND.NXT | Send Next | 发送下一个 (下一个要发送的序列号) |
| SND.WND | Send Window | 发送窗口 |
| SND.UP | Send Urgent Pointer | 发送紧急指针 |
| SND.WL1 | Segment Sequence Number | 用于上次窗口更新的段序列号 |
| SND.WL2 | Segment Acknowledgment Number | 用于上次窗口更新的段确认号 |
| ISS | Initial Send Sequence Number | 初始发送序列号 |
接收序列变量 (Receive Sequence Variables)
| 变量 | 全称 | 说明 |
|---|---|---|
| RCV.NXT | Receive Next | 接收下一个 (下一个期望的序列号) |
| RCV.WND | Receive Window | 接收窗口 |
| RCV.UP | Receive Urgent Pointer | 接收紧急指针 |
| IRS | Initial Receive Sequence Number | 初始接收序列号 |
序列空间图示
发送序列空间 (Send Sequence Space)
1 2 3 4
----------|----------|----------|----------
SND.UNA SND.NXT SND.UNA
+SND.WND
1 - 已确认的旧序列号
2 - 未确认数据的序列号
3 - 允许新数据传输的序列号
4 - 尚不允许的未来序列号
发送窗口: 图中标记为3的序列空间部分
接收序列空间 (Receive Sequence Space)
1 2 3
----------|----------|----------
RCV.NXT RCV.NXT
+RCV.WND
1 - 已确认的旧序列号
2 - 允许新接收的序列号
3 - 尚不允许的未来序列号
接收窗口: 图中标记为2的序列空间部分
当前段变量 (Current Segment Variables)
这些变量从当前段的字段中获取值:
| 变量 | 说明 |
|---|---|
| SEG.SEQ | 段序列号 |
| SEG.ACK | 段确认号 |
| SEG.LEN | 段长度 |
| SEG.WND | 段窗口 |
| SEG.UP | 段紧急指针 |
| SEG.PRC | 段优先级值 |
连接状态 (Connection States)
连接在其生命周期中经历一系列状态。状态包括: LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT 和虚构状态 CLOSED。
状态详细说明
| 状态 | 说明 |
|---|---|
| LISTEN | 表示等待来自任何远程TCP和端口的连接请求 |
| SYN-SENT | 表示在发送连接请求后等待匹配的连接请求 |
| SYN-RECEIVED | 表示在接收和发送连接请求后等待确认连接请求确认 |
| ESTABLISHED | 表示打开的连接,可以将接收的数据交付给用户。连接的数据传输阶段的正常状态 |
| FIN-WAIT-1 | 表示等待来自远程TCP的连接终止请求,或先前发送的连接终止请求的确认 |
| FIN-WAIT-2 | 表示等待来自远程TCP的连接终止请求 |
| CLOSE-WAIT | 表示等待来自本地用户的连接终止请求 |
| CLOSING | 表示等待来自远程TCP的连接终止请求确认 |
| LAST-ACK | 表示等待先前发送到远程TCP的连接终止请求的确认 (其中包括其连接终止请求的确认) |
| TIME-WAIT | 表示等待足够的时间以确保远程TCP收到其连接终止请求的确认 |
| CLOSED | 表示根本没有连接状态 (虚构状态,因为它表示没有TCB时的状态) |
TCP连接状态图
+---------+ ---------\ active OPEN
| CLOSED | \ -----------
+---------+<---------\ \ create TCB
| ^ \ \ snd SYN
passive OPEN | | CLOSE \ \
------------ | | ---------- \ \
create TCB | | delete TCB \ \
V | \ \
+---------+ CLOSE | \
| LISTEN | ---------- | |
+---------+ delete TCB | |
rcv SYN | | SEND | |
----------- | | ------- | V
+---------+ snd SYN,ACK / \ snd SYN +---------+
| |<----------------- ------------------>| |
| SYN | rcv SYN | SYN |
| RCVD |<-----------------------------------------------| SENT |
| | snd ACK | |
| |------------------ -------------------| |
+---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+
| -------------- | | -----------
| x | | snd ACK
| V V
| CLOSE +---------+
| ------- | ESTAB |
| snd FIN +---------+
| CLOSE | | rcv FIN
V ------- | | -------
+---------+ snd FIN / \ snd ACK +---------+
| FIN |<----------------- ------------------>| CLOSE |
| WAIT-1 |------------------ | WAIT |
+---------+ rcv FIN \ +---------+
| rcv ACK of FIN ------- | CLOSE |
| -------------- snd ACK | ------- |
V x V snd FIN V
+---------+ +---------+ +---------+
|FINWAIT-2| | CLOSING | | LAST-ACK|
+---------+ +---------+ +---------+
| rcv ACK of FIN | rcv ACK of FIN |
| rcv FIN -------------- | Timeout=2MSL -------------- |
| ------- x V ------------ x V
\ snd ACK +---------+delete TCB +---------+
------------------------>|TIME WAIT|------------------>| CLOSED |
+---------+ +---------+
TCP连接状态图
事件和状态转换
TCP连接响应事件从一个状态进展到另一个状态。事件包括:
- 用户调用: OPEN, SEND, RECEIVE, CLOSE, ABORT, STATUS
- 传入段: 特别是包含SYN, ACK, RST和FIN标志的段
- 超时: 重传超时、TIME-WAIT超时等
注意: 状态图仅是摘要,不能作为完整规范。它只说明状态变化,以及导致事件和结果操作,但既不处理错误条件,也不处理与状态变化无关的操作。
3.3. Sequence Numbers (序列号)
基本概念
TCP设计中的一个基本概念是通过TCP连接发送的每个数据八位字节都有一个序列号。由于每个八位字节都被排序,因此可以确认它们中的每一个。采用的确认机制是累积的 (cumulative),因此序列号X的确认表示所有八位字节直到但不包括X都已被接收。
这种机制允许在重传存在的情况下进行直接的重复检测。段内八位字节的编号是头部后面的第一个数据八位字节编号最低,后续八位字节连续编号。
序列号空间
关键事实: 实际序列号空间是有限的,尽管非常大。此空间范围从0到2³² - 1。
模运算 (Modulo Arithmetic): 由于空间是有限的,所有处理序列号的算术必须以模2³²执行。这种无符号算术保持序列号从2³² - 1循环到0时的关系。计算机模运算有一些微妙之处,因此在编程此类值的比较时应格外小心。
符号约定:
- 符号
=<表示"小于或等于" (模2³²)
序列号比较
TCP必须执行的典型序列号比较包括:
- 确定确认引用某个已发送但尚未确认的序列号
- 确定段占用的所有序列号都已被确认 (例如,从重传队列中删除段)
- 确定传入段包含预期的序列号 (即,段"重叠"接收窗口)
发送端序列号处理
响应发送数据,TCP将接收确认。需要以下比较来处理确认:
SND.UNA = 最老的未确认序列号
SND.NXT = 下一个要发送的序列号
SEG.ACK = 接收TCP的确认 (接收TCP期望的下一个序列号)
SEG.SEQ = 段的第一个序列号
SEG.LEN = 段中数据占用的八位字节数 (计入SYN和FIN)
SEG.SEQ+SEG.LEN-1 = 段的最后一个序列号
可接受的确认 (Acceptable ACK):
新确认 (称为"可接受的确认") 是满足以下不等式的确认:
SND.UNA < SEG.ACK ≤ SND.NXT
如果段的序列号和长度之和小于或等于传入段中的确认值,则重传队列上的段被完全确认。
示例:
SND.UNA = 1000 (最老未确认)
SND.NXT = 2000 (下一个发送)
收到 SEG.ACK = 1500
检查: 1000 < 1500 ≤ 2000 ✓ (可接受)
收到 SEG.ACK = 2500
检查: 1000 < 2500 ≤ 2000 ✗ (不可接受,确认了未发送的数据)
接收端序列号处理
当接收数据时,需要以下比较:
RCV.NXT = 传入段上期望的下一个序列号,
是接收窗口的左边或下边缘
RCV.NXT+RCV.WND-1 = 传入段上期望的最后一个序列号,
是接收窗口的右边或上边缘
SEG.SEQ = 传入段占用的第一个序列号
SEG.SEQ+SEG.LEN-1 = 传入段占用的最后一个序列号
段的可接受性测试
段如果其序列号在窗口内,则被认为是可接受的。测试取决于段长度和窗口大小:
| 段长度 | 窗口大小 | 可接受性测试 |
|---|---|---|
| 0 | 0 | SEG.SEQ = RCV.NXT |
| 0 | >0 | RCV.NXT ≤ SEG.SEQ < RCV.NXT+RCV.WND |
| >0 | 0 | 不可接受 |
| >0 | >0 | RCV.NXT ≤ SEG.SEQ < RCV.NXT+RCV.WND 或 RCV.NXT ≤ SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND |
说明:
- 零长度段的第一个测试可以看作是对伪段的测试,该伪段从SEG.SEQ开始,并且不占用序列空间
- 如果RCV.WND为零,则不接受数据,但接受不占用空间的段
实际代码示例:
def is_segment_acceptable(seg_seq, seg_len, rcv_nxt, rcv_wnd):
"""检查段是否可接受"""
if seg_len == 0:
if rcv_wnd == 0:
return seg_seq == rcv_nxt
else:
return rcv_nxt <= seg_seq < rcv_nxt + rcv_wnd
else: # seg_len > 0
if rcv_wnd == 0:
return False
else:
# 段的开始或结束在窗口内
start_in_window = rcv_nxt <= seg_seq < rcv_nxt + rcv_wnd
end_in_window = rcv_nxt <= seg_seq + seg_len - 1 < rcv_nxt + rcv_wnd
return start_in_window or end_in_window
初始序列号选择 (ISN)
初始序列号 (ISN) 的选择非常重要。TCP必须使用基于时钟的ISN生成器,以避免旧连接段被误认为是新连接的一部分。
ISN生成建议:
- ISN应该每4微秒增加1
- ISN有大约4.55小时的周期
- 新连接的ISN应该不同于旧连接的ISN
安全考虑:
- 现代实现应使用更安全的ISN生成算法 (RFC 6528)
- 防止序列号预测攻击
关键概念总结
TCP头部结构
- 固定20字节头部: 包含所有核心字段
- 可变长度选项: 最多40字节
- 校验和覆盖伪头部: 提供额外的错误检测
序列号机制
- 每字节编号: 每个数据字节都有唯一的序列号
- 累积确认: 确认号表示所有小于该号的字节已收到
- 模2³²运算: 序列号空间是循环的
连接状态
- 11个状态: 从CLOSED到ESTABLISHED再到CLOSED
- 事件驱动: 用户调用、段到达、超时触发状态转换
- 三次握手: SYN → SYN-ACK → ACK
- 四次挥手: FIN → ACK → FIN → ACK
TCB变量
- 发送变量: SND.UNA, SND.NXT, SND.WND等
- 接收变量: RCV.NXT, RCV.WND等
- 窗口管理: 流量控制的核心
下一部分: 3.4-3.9 Connection Management & Event Processing - 连接建立、关闭、数据通信和事件处理的详细规范