1. TCP头部结构
TCP头部通常为20字节(不含可选字段),每个字段占据固定的字节位置。以下是TCP头部的结构,按字节位置逐一说明:
0 1 2 30 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 (if Data Offset > 5) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段详解(按字节位置)
-
源端口(Source Port, 2字节,0-1字节)
- 作用:标识发送端的端口号,用于区分同一主机上的不同应用。
- 值:16位无符号整数(0-65535)。
- DPDK相关:在DPDK中,解析或构造TCP包时,需从
rte_mbuf
的包数据中提取或设置该字段。
-
目的端口(Destination Port, 2字节,2-3字节)
- 作用:标识接收端的端口号。
- 值:同源端口,16位无符号整数。
- DPDK相关:常与源端口一起用于五元组(源IP、目的IP、协议、源端口、目的端口)进行连接跟踪。
-
序列号(Sequence Number, 4字节,4-7字节)
- 作用:标识发送的数据字节流的起始位置,用于确保数据按序到达和检测丢失。
- 值:32位无符号整数,初始值在SYN包中随机生成。
- DPDK相关:在用户态协议栈中,需手动维护序列号递增逻辑。
-
确认号(Acknowledgment Number, 4字节,8-11字节)
- 作用:表示接收端期望接收的下一个字节的序列号,用于确认已接收的数据。
- 值:32位无符号整数,仅当ACK标志置位时有效。
- DPDK相关:在ACK包处理中,需检查确认号以验证数据传输状态。
-
数据偏移(Data Offset, 4位,12字节高4位)
- 作用:表示TCP头部长度(以4字节为单位),因为头部可能包含可选字段。
- 值:范围5-15(对应20-60字节)。
- DPDK相关:解析TCP包时,需根据此字段确定头部长度以定位数据部分。
-
保留位(Reserved, 6位,12字节次高6位)
- 作用:保留供未来使用,当前必须置0。
- DPDK相关:通常忽略,但需确保构造包时置0。
-
标志位(Flags, 6位,12字节低6位)
- 作用:控制TCP连接状态和数据传输行为,详细见下文“标志位详解”。
- 值:包括URG、ACK、PSH、RST、SYN、FIN六个标志。
-
窗口大小(Window, 2字节,13-14字节)
- 作用:表示接收端的接收缓冲区大小,用于流量控制。
- 值:16位无符号整数(0-65535字节),可通过窗口扩展选项放大。
- DPDK相关:在高性能场景中,需根据窗口大小动态调整发送速率。
-
校验和(Checksum, 2字节,15-16字节)
- 作用:验证TCP头部、数据及伪头部的完整性。
- 值:16位校验和,计算方法见前文。
- DPDK相关:可通过DPDK的
rte_ipv4_udptcp_cksum
计算,或使用网卡硬件卸载(如PKT_TX_TCP_CKSUM
)。
-
紧急指针(Urgent Pointer, 2字节,17-18字节)
- 作用:当URG标志置位时,指示紧急数据的偏移量。
- 值:16位无符号整数,指向紧急数据的最后一个字节。
- DPDK相关:紧急数据在现代应用中较少使用,通常忽略。
-
选项(Options, 可变长度,19字节起,若Data Offset > 5)
- 作用:提供扩展功能,如最大段大小(MSS)、窗口扩展、时间戳等。
- 长度:0-40字节,需4字节对齐。
- DPDK相关:在构造或解析包时,需根据Data Offset处理选项字段。
2. TCP标志位(Flags)的名称由来和用法
TCP头部中的6个标志位(URG、ACK、PSH、RST、SYN、FIN)位于12字节的低6位,每个标志位占1位。以下是它们的名称由来和具体用法:
-
URG(Urgent, 紧急标志)
- 名称由来:表示数据包中包含紧急数据,需要优先处理。“Urgent”反映了其紧急优先级。
- 用法:
- 当URG=1时,紧急指针字段有效,指示紧急数据的结束位置。
- 紧急数据应被立即处理,通常用于中断或控制场景(如Telnet的Ctrl+C)。
- 现代场景:很少使用,部分协议栈甚至忽略此标志。
- DPDK相关:在DPDK中,URG标志处理较少,需检查
struct rte_tcp_hdr
的tcp_flags
字段。
-
ACK(Acknowledgment, 确认标志)
- 名称由来:表示确认已接收的数据。“Acknowledgment”指接收端对发送端数据的确认。
- 用法:
- 当ACK=1时,确认号字段有效,表示接收端期望的下一个字节序列号。
- 用于确认数据接收,驱动TCP的可靠传输机制。
- 在三次握手中,第二次和第三次握手包会设置ACK=1。
- DPDK相关:在用户态协议栈中,需检查ACK标志以处理确认逻辑。
-
PSH(Push, 推送标志)
- 名称由来:指示接收端立即将数据“推送”到应用层,而不等待缓冲区填满。“Push”强调数据尽快交付。
- 用法:
- 当PSH=1时,提示接收端尽快将数据交给上层应用,而不是缓存。
- 常用于实时性要求高的场景(如交互式应用)。
- 现代场景:许多协议栈默认立即交付,PSH作用减少。
- DPDK相关:在DPDK中,PSH标志可用于优化数据交付逻辑。
-
RST(Reset, 重置标志)
- 名称由来:表示重置连接,通常用于异常终止。“Reset”反映了其强制中断连接的角色。
- 用法:
- 当RST=1时,表示连接异常或拒绝(如端口未开放、连接不可用)。
- 用于快速关闭连接或拒绝非法请求。
- DPDK相关:在DPDK中,需检测RST标志以处理连接错误或终止状态。
-
SYN(Synchronize, 同步标志)
- 名称由来:用于同步通信双方的序列号。“Synchronize”反映了其初始化序列号的作用。
- 用法:
- 当SYN=1时,表示发起TCP连接,携带初始序列号(ISN)。
- 用于三次握手的第一步(客户端发送SYN)和第二步(服务器发送SYN+ACK)。
- DPDK相关:在DPDK中,构造SYN包需设置
tcp_flags
中的SYN位,并初始化序列号。
-
FIN(Finish, 结束标志)
- 名称由来:表示发送端已完成数据发送,请求关闭连接。“Finish”反映了其终止连接的含义。
- 用法:
- 当FIN=1时,表示发送端无更多数据要发送,请求关闭连接。
- 用于四次挥手中的第一步和第三步,完成连接的正常关闭。
- DPDK相关:在DPDK中,需处理FIN标志以管理连接关闭流程。
3. DPDK中的TCP头部处理
在DPDK开发中,TCP头部的解析和构造是常见操作,以下是相关要点:
- 头部解析:通过
rte_mbuf
访问数据包,使用struct rte_tcp_hdr
结构解析TCP头部字段。例如:struct rte_tcp_hdr *tcp_hdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_tcp_hdr *, ip_hdr_len); uint8_t tcp_flags = tcp_hdr->tcp_flags; // 获取标志位
- 标志位操作:DPDK提供了宏(如
RTE_TCP_SYN_FLAG
、RTE_TCP_ACK_FLAG
)来检查或设置标志位。例如:if (tcp_hdr->tcp_flags & RTE_TCP_SYN_FLAG) {// 处理SYN包 }
- 校验和处理:可使用
rte_ipv4_udptcp_cksum
计算校验和,或启用网卡硬件卸载(PKT_TX_TCP_CKSUM
)。 - 选项处理:根据
data_off
字段(右移4位得到头部长度)解析选项字段,需注意字节对齐。
4. 总结
- TCP头部结构:共20字节(无选项),包括源/目的端口、序列号、确认号、数据偏移、标志位、窗口大小、校验和、紧急指针等字段。
- 标志位由来与用法:
- URG:紧急数据,优先处理(少用)。
- ACK:确认接收,驱动可靠传输。
- PSH:推送数据,加快交付。
- RST:重置连接,处理异常。
- SYN:同步序列号,建立连接。
- FIN:结束连接,正常关闭。
- DPDK相关:在DPDK中,需解析/构造TCP头部,处理标志位以实现连接管理,结合硬件卸载优化性能。
值得注意的是:
在 TCP 的三次握手过程中,第二次握手(即服务端回复 SYN+ACK 报文)中的 ACK 通常会是对方初始序列号加 1。原因是,TCP 协议中 SYN 和 FIN 这类控制位虽然不携带有效数据,但它们本身会占用一个序列号位置,也就是说 SYN 报文等价于“长度为 1 的虚拟数据”。
因此,客户端第一次发送的 SYN 报文中若使用初始序列号 x,服务端在回复 ACK 时就必须设置为 x+1,表示已经“收到”了这个 SYN。
相反,在正常的数据通信过程中,一个不携带任何数据的 ACK 报文(例如仅用于确认收到某段数据)不会额外消耗序列号。它只是对前面数据的确认,因此 ACK 值不会额外加 1,除非前一个报文中确实包含了数据或 FIN、SYN 这类控制位。
所以,并不是“ACK 就一定要 +1”,而是“如果前一个报文包含了 SYN、FIN 或数据,就要按其长度加 1 或加对应长度”。三次握手中之所以出现 +1,只是因为 SYN 报文本身占了一个序号位置。这个现象容易被误解为“ACK 报文默认要加 1”,实质上是对 TCP 序列号含义的不准确理解。