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

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 Port16 bit源端口号 (0-65535)
Destination Port16 bit目的端口号 (0-65535)
Sequence Number32 bit数据的序列号
Acknowledgment Number32 bit期望收到的下一个序列号
Data Offset4 bit头部长度(单位:4字节)
Reserved3 bit保留位
Flags9 bit控制标志
Window16 bit接收窗口大小
Checksum16 bit校验和
Urgent Pointer16 bit紧急指针

3.3 TCP 标志位 (Flags)

标志全称说明
SYNSynchronize同步序列号,建立连接
ACKAcknowledgment确认号有效
FINFinish发送方数据发送完毕
RSTReset重置连接
PSHPush推送数据,立即交付
URGUrgent紧急数据,Urgent Pointer 有效
CWRCongestion Window Reduced拥塞窗口减少
ECEECN-EchoECN 回显
三次握手标志位:
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)

选项类型长度说明
EOL01选项结束
NOP11填充
MSS24最大段大小
Window Scale33窗口缩放因子
SACK Permitted42允许选择性确认
SACK5可变选择性确认块
Timestamp810时间戳
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 连接管理 - 三次握手与四次挥手