未分类 · 2025-05-31 0

使用 tcpdump 分析 seq 和 ack

一、分析连接过程

root@225f19598cc3:/# tcpdump -v -n
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

# 客户端 → 服务端:SYN 包。客户端发起连接请求,标志位为 [S](SYN),表示同步请求,初始序列号 seq=167702838,MSS(最大报文段长度)为 1460 字节
# 协议类型和 IP 头部信息
# tos 0x0:服务类型
# ttl 64:生存时间.每经过一个路由器减 1,减到 0 时丢弃。此处为 64,说明该数据包最多还能经过 64 跳
# id 55449:IP ID(标识符),用于唯一标识当前主机发送的数据报,主要用于分片重组。
# offset 0:分片偏移量,表示该数据包在原始数据报中的位置。这里为 0,表示这是第一个(也是唯一一个)分片。
# flags [DF]:标志位,DF 表示 Don't Fragment(不分片)。TCP 通常会设置此标志以避免分片。
# proto TCP (6):上层协议为 TCP,协议号为 6。
# length 60:P 总长度为 60 字节,包括 IP 头(20 字节) + TCP 头(20 字节)+ 可能的选项部分(20 字节)。
# Flags [S]:[S] 表示 SYN 标志位被置 1。
# seq 167702838:Sequence Number(序列号):本次发送的第一个字节的编号,用于保证顺序可靠传输。初始值随机生成,这里是 167702838。
# win 64240:Window Size(窗口大小):接收方当前可以接收的数据量,单位是字节。表示客户端目前允许服务端发送最多 64240 字节 的数据而不必等待确认。
# options:TCP 选项(Options)。
# length 0:表示 TCP 数据部分长度为 0,这是一个纯控制包(SYN),没有携带应用层数据。
09:25:52.414604 IP (tos 0x0, ttl 64, id 55449, offset 0, flags [DF], proto TCP (6), length 60)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [S], cksum 0x5856 (incorrect -> 0x7098), seq 167702838, win 64240, options [mss 1460,sackOK,TS val 2618638934 ecr 0,nop,wscale 7], length 0

# 服务端 → 客户端:SYN-ACK 包。服务端回应连接请求,标志位为 [S.](SYN + ACK),序列号 seq=4227940362,确认号 ack=167702839(即客户端初始序列号 + 1)。
09:25:52.414621 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    172.18.0.2.80 > 172.18.0.1.43614: Flags [S.], cksum 0x5856 (incorrect -> 0x5e4c), seq 4227940362, ack 167702839, win 65160, options [mss 1460,sackOK,TS val 476034615 ecr 2618638934,nop,wscale 7], length 0

# 客户端 → 服务端:ACK 包。客户端发送确认包,标志位为 [.](仅 ACK),确认号 ack=1,表示对服务端序列号的确认,此时 TCP 连接已建立。
09:25:52.414638 IP (tos 0x0, ttl 64, id 55450, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [.], cksum 0x584e (incorrect -> 0x89ab), ack 1, win 502, options [nop,nop,TS val 2618638934 ecr 476034615], length 0

# 客户端 → 服务端:HTTP GET 请求。客户端发送 HTTP GET 请求,标志位为 [P.](PSH + ACK),表示有数据推送,数据内容是 HTTP 请求头,请求根路径 /。
# IP length = 126:表示整个 IP 包的长度为 126 字节。表示整个 IP 包的长度为 126 字节。IP 头部(通常 20 字节),TCP 头部(通常 20 字节 + 选项如 TS),数据部分(74 字节)。
# TCP length = 74:表示 TCP 的数据部分(即 HTTP 请求头)长度是 74 字节。
09:25:52.414679 IP (tos 0x0, ttl 64, id 55451, offset 0, flags [DF], proto TCP (6), length 126)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [P.], cksum 0x5898 (incorrect -> 0x864f), seq 1:75, ack 1, win 502, options [nop,nop,TS val 2618638934 ecr 476034615], length 74: HTTP, length: 74
    GET / HTTP/1.1
    Host: 172.18.0.2
    User-Agent: curl/7.81.0
    Accept: */*

# 服务端 → 客户端:ACK 确认。服务端确认收到客户端的数据。
09:25:52.414684 IP (tos 0x0, ttl 64, id 37395, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.2.80 > 172.18.0.1.43614: Flags [.], cksum 0x584e (incorrect -> 0x895a), ack 75, win 509, options [nop,nop,TS val 476034615 ecr 2618638934], length 0

# 服务端 → 客户端:HTTP 响应头。内容长度为 13 字节,说明正文很小。
09:25:52.414809 IP (tos 0x0, ttl 64, id 37396, offset 0, flags [DF], proto TCP (6), length 287)
    172.18.0.2.80 > 172.18.0.1.43614: Flags [P.], cksum 0x5939 (incorrect -> 0xec06), seq 1:236, ack 75, win 509, options [nop,nop,TS val 476034615 ecr 2618638934], length 235: HTTP, length: 235
    HTTP/1.1 200 OK
    Server: nginx/1.24.0
    Date: Mon, 14 Jul 2025 09:25:52 GMT
    Content-Type: text/html
    Content-Length: 13
    Last-Modified: Tue, 08 Jul 2025 09:16:33 GMT
    Connection: keep-alive
    ETag: "686ce1f1-d"
    Accept-Ranges: bytes

# 客户端 → 服务端:ACK 确认。
09:25:52.414829 IP (tos 0x0, ttl 64, id 55452, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [.], cksum 0x584e (incorrect -> 0x8877), ack 236, win 501, options [nop,nop,TS val 2618638934 ecr 476034615], length 0

# 服务端 → 客户端:HTTP 响应体(正文)。服务端发送响应正文,共 13 字节。
09:25:52.414854 IP (tos 0x0, ttl 64, id 37397, offset 0, flags [DF], proto TCP (6), length 65)
    172.18.0.2.80 > 172.18.0.1.43614: Flags [P.], cksum 0x585b (incorrect -> 0x2c6b), seq 236:249, ack 75, win 509, options [nop,nop,TS val 476034615 ecr 2618638934], length 13: HTTP

# 客户端 → 服务端:ACK 确认。客户端再次确认收到全部数据。
09:25:52.414866 IP (tos 0x0, ttl 64, id 55453, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [.], cksum 0x584e (incorrect -> 0x886a), ack 249, win 501, options [nop,nop,TS val 2618638934 ecr 476034615], length 0

# 客户端 → 服务端:FIN 包。客户端发送 FIN 包,表示要关闭连接,标志位为 [F.](FIN + ACK),序列号为 75,表示这是最后一个数据字节。
09:25:52.414960 IP (tos 0x0, ttl 64, id 55454, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [F.], cksum 0x584e (incorrect -> 0x8869), seq 75, ack 249, win 501, options [nop,nop,TS val 2618638934 ecr 476034615], length 0

# 服务端 → 客户端:ACK 确认。服务端回应一个 ACK,并发送自己的 FIN 包,标志位为 [F.](FIN + ACK),确认号为 76,表示已接收客户端的 FIN。
09:25:52.414991 IP (tos 0x0, ttl 64, id 37398, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.2.80 > 172.18.0.1.43614: Flags [F.], cksum 0x584e (incorrect -> 0x8860), seq 249, ack 76, win 509, options [nop,nop,TS val 476034615 ecr 2618638934], length 0

# 客户端 → 服务端:ACK 确认。客户端发送最终的 ACK,完成四次挥手,连接完全关闭。
09:25:52.415010 IP (tos 0x0, ttl 64, id 55455, offset 0, flags [DF], proto TCP (6), length 52)
    172.18.0.1.43614 > 172.18.0.2.80: Flags [.], cksum 0x584e (incorrect -> 0x8868), ack 250, win 501, options [nop,nop,TS val 2618638934 ecr 476034615], length 0

二、TCP 连接全过程 seq 与 ack 变化表

在第一次挥手之后,如果被动方没有数据要发给主动方。第二和第三次挥手是有可能合并传输的,服务端把 ACK 和 FIN 放在一个包里(即合并了第 2 和第 3 步)。这样就出现了三次挥手。

方向 标志位 seq 值(起始) seq 范围 数据长度 ack seq 来源说明 ack 来源说明
C→S [S] 167702838 - 0 - 客户端初始 SEQ(随机生成) 无 ACK(SYN 包)
S→C [S.] 4227940362 - 0 167702839 服务端初始 SEQ(随机生成) 客户端 SEQ + 1(SYN 占一个字节)
C→S [.] 167702839 - 0 4227940363 上一次客户端 SEQ + 1(SYN 占一个字节) 服务端 SEQ + 1(SYN 占一个字节)
C→S [P.] 1 1:75 74 1 上一次客户端 SEQ(167702839)后开始新数据流,起始为 1(可能是相对序号) 上一次服务端 SEQ(4227940362)+1 后已确认
S→C [.] 1 - 0 75 上一次服务端 SEQ(4227940362)后开始新数据流,起始为 1(相对序号) 客户端上一次 SEQ(1)+ 数据长度(74)
S→C [P.] 1 1:236 235 75 继续使用当前 SEQ 保持不变
C→S [.] 75 - 0 236 上一次客户端 SEQ(1)+ 数据长度(74) 服务端上一次 SEQ(1)+ 数据长度(235)
S→C [P.] 236 236:249 13 75 上一次服务端 SEQ(1)+ 数据长度(235) 保持不变
C→S [.] 75 - 0 249 保持不变 服务端上一次 SEQ(236)+ 数据长度(13)
C→S [F.] 75 - 0 249 保持不变 保持不变
S→C [.] 249 - 0 76 上一次服务端 SEQ(236)+ 数据长度(13) 客户端 FIN 的 SEQ(75)+1
S→C [F.] 249 - 0 76 保持不变 保持不变
C→S [.] 76 - 0 250 客户端 FIN 的 SEQ(75)+1 服务端 FIN 的 SEQ(249)+1

1.初始连接(三次握手)

SYN 报文**会占用一个序列号(即使没有数据)

  • 客户端发送 SYN:seq = 167702838
  • 服务端回复 SYN/ACK:seq = 4227940362, ack = 167702838 + 1 = 167702839
  • 客户端最后 ACK:seq = 167702838 + 1 = 167702839, ack = 4227940362 + 1 = 4227940363

2.数据传输阶段

HTTP 请求(GET /)

  • 客户端发送数据:seq = 1(可能基于连接建立后的相对序号)
  • 数据长度:74 字节 → 下一个期望收到的序号是 1 + 74 = 75
  • 服务端返回 ACK:ack = 75

服务端响应(HTTP 200 OK)

  • 第一包数据:seq = 1, 长度 235 → 下一个是 1 + 235 = 236

  • 客户端返回 ACK:ack = 236

  • 第二包数据:seq = 236, 长度 13 → 下一个是 236 + 13 = 249

  • 客户端返回 ACK:ack = 249

3.四次挥手

客户端发送 FIN

  • 当前客户端最后一个数据 SEQ 是 75(上次发送的是 HTTP 请求结束位置)
  • FIN 标志也占一个 SEQ → 所以发送 seq = 75, Flags [F.]

服务端回应 ACK

  • 确认客户端 FIN:ack = 75 + 1 = 76

服务端发送 FIN

  • 当前服务端最后一个数据 SEQ 是 249(上一个数据包结束在 249)
  • 发送 seq = 249, Flags [F.]

客户端回应 ACK

  • 确认服务端 FIN:ack = 249 + 1 = 250
类型 seq 增长方式 ack 计算方式
普通数据段 当前 seq + 数据长度 对方 seq + 数据长度
SYN/FIN 标志 占用 1 个 seq 对方 seq + 1
纯 ACK(无数据) 不增长 对方 seq + 数据长度(或 +1 若有 SYN/FIN)

三、tcpdump 参数

tcpdump -i eth0 -nn port 80 -A -s 0
tcpdump -i eth0 -nn port 80 -X -s 0
tcpdump -i any -nn port 80 -X -s 0

如果你没有加上 -X、-xx、-A 等参数,tcpdump 默认不会打印 payload(正文)内容

  • -i eth0: 指定网卡
  • -i any:监听所有网卡
  • -nn: 不解析主机名和服务名
  • port 80: 只抓 HTTP 流量
  • -A: 以 ASCII 形式显示数据内容
  • -s 0: 抓取完整包(不受 snaplen 截断)

四、常见的 TCP Flags 标志位

Flag 缩写 含义
SYN [S] 同步序号,用于发起一个连接。
ACK [.] 确认序号有效。通常单独显示为点(.),但在其他标志存在时会明确写出。
FIN [F] 发送端已经完成数据发送,请求关闭连接。
RST [R] 重置连接。用于异常终止连接。
PSH [P] 推送功能,提示接收方尽快将数据提交给应用程序,而不是等待更多的数据到来。
URG [U] 紧急指针字段有效,表示该数据段中有紧急数据。
ECE [E] ECN-Echo,用于显式拥塞通知机制。
CWR [C] 拥塞窗口减少,用于显式拥塞通知机制。