强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

TCP/UDP 网络协议教程 / 03-TCP 头部详解

03 - TCP 头部详解

3.1 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 |       |C|E|U|A|P|R|S|F|                               |
| Offset| Rsrvd |W|C|R|C|S|S|Y|I|            Window             |
|  (4)  |  (3)  |R|E|G|K|H|T|N|N|            (16)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (0-40 bytes)                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.2 头部字段详解

字段 长度 说明
Source Port 16 bit 源端口号 (0-65535)
Destination Port 16 bit 目的端口号 (0-65535)
Sequence Number 32 bit 数据的序列号
Acknowledgment Number 32 bit 期望收到的下一个序列号
Data Offset 4 bit 头部长度(单位:4字节)
Reserved 3 bit 保留位
Flags 9 bit 控制标志
Window 16 bit 接收窗口大小
Checksum 16 bit 校验和
Urgent Pointer 16 bit 紧急指针

3.3 TCP 标志位 (Flags)

标志 全称 说明
SYN Synchronize 同步序列号,建立连接
ACK Acknowledgment 确认号有效
FIN Finish 发送方数据发送完毕
RST Reset 重置连接
PSH Push 推送数据,立即交付
URG Urgent 紧急数据,Urgent Pointer 有效
CWR Congestion Window Reduced 拥塞窗口减少
ECE ECN-Echo ECN 回显
三次握手标志位:
Client → Server: SYN=1, Seq=x
Server → Client: SYN=1, ACK=1, Seq=y, Ack=x+1
Client → Server: ACK=1, Seq=x+1, Ack=y+1

四次挥手标志位:
Client → Server: FIN=1, ACK=1, Seq=u, Ack=v
Server → Client: ACK=1, Seq=v, Ack=u+1
Server → Client: FIN=1, ACK=1, Seq=w, Ack=u+1
Client → Server: ACK=1, Seq=u+1, Ack=w+1

3.4 序列号与确认号

序列号 (Sequence Number)

# 序列号的工作方式
# 初始序列号 (ISN) 是随机生成的

# 发送数据时,序列号递增
# 假设 ISN = 1000,发送 100 字节数据:

# 发送第1个段:Seq=1000, 数据=100字节
# 发送第2个段:Seq=1100, 数据=100字节
# 发送第3个段:Seq=1200, 数据=100字节

def calculate_next_seq(current_seq, data_len):
    """计算下一个序列号"""
    return current_seq + data_len

# 示例
seq = 1000
for i in range(3):
    next_seq = calculate_next_seq(seq, 100)
    print(f"发送: Seq={seq}, 下一个Seq={next_seq}")
    seq = next_seq

确认号 (Acknowledgment Number)

确认号 = 期望收到的下一个字节的序列号

示例:
Host A 发送:Seq=1000, 100字节
Host B 回复:Ack=1100(表示:我已收到 1000-1099,期望收到 1100)

Host A 发送:Seq=1100, 200字节
Host B 回复:Ack=1300(表示:我已收到 1100-1299,期望收到 1300)

3.5 窗口大小 (Window Size)

# 窗口大小用于流量控制
# 接收方通过窗口大小告诉发送方:我还能接收多少数据

def flow_control_example():
    """窗口大小变化示例"""
    
    receiver_buffer = 10000  # 接收缓冲区大小
    received_data = 0
    
    print(f"初始窗口大小: {receiver_buffer}")
    
    # 模拟数据接收
    for i in range(5):
        incoming_data = 2000
        received_data += incoming_data
        window = max(0, receiver_buffer - received_data)
        
        print(f"接收 {incoming_data} 字节后,窗口大小: {window}")
        
        # 模拟应用层读取数据
        if received_data > 5000:
            read_size = 3000
            received_data -= read_size
            print(f"  应用层读取 {read_size} 字节,窗口恢复: {receiver_buffer - received_data}")

flow_control_example()
输出:
初始窗口大小: 10000
接收 2000 字节后,窗口大小: 8000
接收 2000 字节后,窗口大小: 6000
接收 2000 字节后,窗口大小: 4000
接收 2000 字节后,窗口大小: 2000
  应用层读取 3000 字节,窗口恢复: 5000
接收 2000 字节后,窗口大小: 3000

3.6 校验和 (Checksum)

def tcp_checksum(src_ip, dst_ip, tcp_segment):
    """计算 TCP 校验和(含伪头部)"""
    import struct
    
    # 伪头部
    src = socket.inet_aton(src_ip)
    dst = socket.inet_aton(dst_ip)
    placeholder = 0
    protocol = 6  # TCP
    tcp_len = len(tcp_segment)
    
    pseudo_header = struct.pack('!4s4sBBH', src, dst, placeholder, protocol, tcp_len)
    
    # 计算校验和
    data = pseudo_header + tcp_segment
    
    if len(data) % 2:
        data += b'\x00'
    
    checksum = 0
    for i in range(0, len(data), 2):
        word = (data[i] << 8) + data[i+1]
        checksum += word
    
    checksum = (checksum >> 16) + (checksum & 0xFFFF)
    checksum = ~checksum & 0xFFFF
    
    return checksum
TCP 校验和覆盖范围:
┌─────────────────────────────────────┐
│         伪头部 (12 字节)            │
│  ┌─────────────────────────────┐   │
│  │ 源 IP (4) │ 目的 IP (4)    │   │
│  │ 保留 (1) │ 协议 (1) │ 长度(2)│   │
│  └─────────────────────────────┘   │
├─────────────────────────────────────┤
│         TCP 头部 + 数据             │
└─────────────────────────────────────┘

3.7 TCP 选项 (Options)

选项 类型 长度 说明
EOL 0 1 选项结束
NOP 1 1 填充
MSS 2 4 最大段大小
Window Scale 3 3 窗口缩放因子
SACK Permitted 4 2 允许选择性确认
SACK 5 可变 选择性确认块
Timestamp 8 10 时间戳
MSS 协商示例:
Client → Server: SYN, MSS=1460
Server → Client: SYN+ACK, MSS=1460

实际 MSS = min(1460, 1460) = 1460 字节

3.8 头部解析代码

import struct
import socket

def parse_tcp_header(data):
    """解析 TCP 头部"""
    if len(data) < 20:
        return None
    
    # 解析固定头部(20字节)
    fields = struct.unpack('!HHIIBBHHH', data[:20])
    
    src_port = fields[0]
    dst_port = fields[1]
    seq_num = fields[2]
    ack_num = fields[3]
    data_offset = (fields[4] >> 4) * 4
    flags = fields[5]
    window = fields[6]
    checksum = fields[7]
    urgent_ptr = fields[8]
    
    # 解析标志位
    flag_names = []
    if flags & 0x01: flag_names.append('FIN')
    if flags & 0x02: flag_names.append('SYN')
    if flags & 0x04: flag_names.append('RST')
    if flags & 0x08: flag_names.append('PSH')
    if flags & 0x10: flag_names.append('ACK')
    if flags & 0x20: flag_names.append('URG')
    
    return {
        'src_port': src_port,
        'dst_port': dst_port,
        'seq_num': seq_num,
        'ack_num': ack_num,
        'data_offset': data_offset,
        'flags': flag_names,
        'window': window,
        'checksum': f'0x{checksum:04x}',
        'urgent_pointer': urgent_ptr,
        'options': data[20:data_offset]
    }

# 使用示例
header = parse_tcp_header(tcp_data)
if header:
    print(f"{header['src_port']}{header['dst_port']}")
    print(f"Seq={header['seq_num']}, Ack={header['ack_num']}")
    print(f"Flags: {', '.join(header['flags'])}")
    print(f"Window: {header['window']}")

3.9 注意事项

⚠️ 序列号溢出:32 位序列号在高速网络中可能回绕,需要使用 PAWS(Protection Against Wrapped Sequences)机制

⚠️ 窗口缩放:Window Scale 选项只在 SYN 包中协商,连接建立后不可更改

⚠️ 头部长度:Data Offset 字段最大值为 15,因此 TCP 头部最大为 60 字节

3.10 业务场景

场景 关注的头部字段
防火墙规则 源/目的端口、标志位
负载均衡 源/目的端口、IP 地址
抓包分析 序列号、确认号、窗口大小
性能优化 窗口大小、MSS、SACK
安全审计 标志位组合(如 SYN Flood)

3.11 扩展阅读


下一章04 - TCP 连接管理 - 三次握手与四次挥手