TCP头部字段详解
1. 源端口和目的端口(各16位)
-
功能:标识发送和接收应用程序
-
范围:0-65535(0-1023为知名端口)
-
技术细节:
-
客户端通常使用临时端口(1024-65535)
-
服务端使用固定端口(如HTTP=80, HTTPS=443)
-
支持端口复用(SO_REUSEPORT选项)
-
// Linux内核端口选择算法 (net/ipv4/inet_connection_sock.c)
int inet_csk_get_port(struct sock *sk, unsigned short snum) {
// ... 实现端口自动选择和冲突处理
}
2. 序列号(32位)
-
功能:标识发送的数据字节流
-
初始序列号(ISN):基于时钟的随机算法(RFC 6528)
-
防止序列号预测攻击
-
避免旧连接的报文干扰
-
-
内核实现:
// Linux ISN生成算法 (net/ipv4/tcp_ipv4.c) u32 secure_tcp_seq(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) { // 使用SipHash加密算法生成 }
-
回绕处理:当序列号达到2^32-1时回绕到0
3. 确认号(32位)
-
功能:期望收到的下一个字节的序列号
-
累积确认机制:确认号N表示所有小于N的字节已收到
-
选择性确认(SACK):通过TCP选项实现非连续确认
4. 数据偏移(4位)
-
功能:指定TCP头部长度(以4字节为单位)
-
计算:头部长度 = 数据偏移 × 4
-
范围:5(最小20字节)到15(最大60字节)
5. 保留位(4位)
-
必须置0,为未来协议扩展保留
-
实际使用中可能被实验性协议使用(需IANA批准)
6. 控制标志(9位)
标志位 | 名称 | 功能 | 应用场景 |
---|---|---|---|
URG | 紧急指针 | 指示紧急数据处理 | Telnet中断命令 |
ACK | 确认 | 确认号字段有效 | 所有数据传输 |
PSH | 推送 | 要求立即交付数据 | 实时交互应用 |
RST | 重置 | 立即终止连接 | 端口扫描防御 |
SYN | 同步 |
标志组合示例:
-
SYN+ACK:连接建立响应
-
FIN+ACK:正常连接终止
-
PSH+ACK:即时数据传输
7. 窗口大小(16位)
-
功能:接收方通告的可用缓冲区大小
-
流量控制机制:动态调整发送速率
-
窗口缩放选项:通过选项扩展窗口大小(最高1GB)
// Linux内核窗口缩放处理 (net/ipv4/tcp_input.c) void tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb) { // ... 处理窗口缩放选项 }
-
零窗口探测:当窗口为0时发送探测报文
8. 校验和(16位)
10. 选项字段(可变长度)
选项结构:
+--------+--------+--------+--------+ | Kind | Length | Data... | +--------+--------+--------+--------+
核心选项详解:
-
覆盖范围:TCP头部+数据+伪头部
-
伪头部结构:
0 7 8 15 16 23 24 31 +--------+--------+--------+--------+ | source address | +--------+--------+--------+--------+ | destination address | +--------+--------+--------+--------+ | zero | PTCL | TCP length | +--------+--------+--------+--------+
-
计算算法:
uint16_t tcp_checksum(const void *buff, size_t len, in_addr_t src_addr, in_addr_t dest_addr) { uint32_t sum = 0; const uint16_t *ptr = buff; // 伪头部计算 struct pseudo_header psh = {src_addr, dest_addr, htons(IPPROTO_TCP), htons(len)}; sum = calculate(&psh, sizeof(psh)); // 数据计算 while (len > 1) { sum += *ptr++; len -= 2; } if (len > 0) sum += (*ptr) & htons(0xFF00); // 折叠进位 sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return (uint16_t)(~sum); }
9. 紧急指针(16位)
-
功能:当URG=1时,指示紧急数据结束位置
-
计算:紧急数据结束 = 序列号 + 紧急指针
-
实际应用:SSH/Telnet的中断命令(Ctrl+C)
Kind | 名称 | 长度 | 功能 |
---|---|---|---|
0 | 选项结束 | 1 | 选项列表结束 |
1 | 无操作 | 1 | 选项填充 |
2 | 最大报文段 | 4 | 协商MSS(通常1460) |
3 | 窗口扩大因子 | 3 | 窗口缩放(2^14倍) |
4 | 选择性确认 | 2 | 启用SACK功能 |
5 | SACK块 | 可变 | 非连续接收的数据块 |
8 | 时间戳 | 10 | RTT测量和PAWS保护 |
28 | 用户超时 | 4 | 连接超时设置 |
29 | 认证 | 可变 | TCP-AO安全认证 |
以超时重传为例,假设客户端(Client)向服务器(Server)发送数据包,但未收到ACK确认。
场景步骤:
Client发送数据包(初始传输):
- TCP头部包含选项字段(如时间戳):
数据偏移 = 8(头部总长32字节 = 8 × 4) 选项字段:时间戳(Timestamp)值 = T1 序列号(Seq) = 100 数据 = "Hello"
- 数据包结构:
| 20字节固定头部 | 12字节选项(时间戳) | 数据 "Hello" |
Server未收到该包(可能因网络拥塞丢失):
- Client在重传超时(RTO) 后未收到ACK,触发重传。
Client重传数据包
数据偏移 = 8(长度不变)
选项字段:时间戳值 = T2(T2 > T1)
序列号(Seq)仍为 100(相同数据)
数据 = "Hello"(不变)
关键点分析:
- 选项长度一致性:重传时
数据偏移
值仍为8
,表明头部长度未变(选项字段仍存在)。 - 选项内容变化:时间戳更新(
T1 → T2
),帮助计算RTT(往返时间)和重传延迟。 - 接收方处理:Server通过
数据偏移=8
跳过32字节头部,直接解析数据"Hello"。