4.2 TCP 连接的一生:握手、传数、挥手与账本


4.2 TCP 连接的一生:握手、传数、挥手与账本

外两层讲完"路",本节讲"连接"本身。TCP 是排障案里出镜率最高的协议,它的头 20 字节加上几个选项,承载了可靠性工程的全部机关:握手定参数、序号记账本、窗口控流量、挥手清现场。本节沿时间轴解剖一条连接的一生,并建立"账本"视角——第 8 章判定重传、乱序、零窗口,用的全是这本账。

出生:三次握手与它的选项谈判

抓包现场的三帧(用相对序号,Wireshark 默认显示方式):

$ tshark -r conn.pcapng -Y "tcp.stream==0 && tcp.flags.syn==1" -T fields \ -e frame.number -e tcp.flags -e tcp.seq_raw -e tcp.options 3 0x000002 (SYN) 3316898041 MSS=1460;WS=7;SACK_PERM;TSval...;Nop;Nop 4 0x000012 (SYN,ACK) 4081221925 MSS=1460;WS=7;SACK_PERM;TSval... 5 0x000010 (ACK) 3316898042 空载荷确认 选项已停止携带 $ tshark -r conn.pcapng -Y "tcp.stream==0 && tcp.flags.syn==1" -T fields -e tcp.window_size_value -e tcp.options.wscale.val 64240 7 28960 7

握手不只是"确认彼此在线",更是参数谈判:MSS 是每段最大载荷字节数(以太网典型 1460);WS 是窗口缩放因子(7 表示真实窗口等于头部值乘 2 的 7 次方,即 128 倍);SACK_PERM 声明支持选择性确认。窗口缩放只出现在 SYN 与 SYN+ACK 里,错过握手就永远推不出真实窗口——这是排障铁律"要抓就抓到 SYN"的字节层原因。

序号账本从握手开始记账:客户端 SYN 占一个序号,所以 SYN+ACK 的确认号是 x+1。这两个"虚拟字节"是初学者推演序号时最容易漏算的。

成长:传数据与滑动窗口

握手完成后进入传数阶段。TCP 头的序号与确认号分工明确:序号是"我发的这段数据的第一个字节编号",确认号是"你发给我的我已全部收齐到第几字节,期待下一个"。配合头部校验后的窗口字段,就构成了滑动窗口机制——接收方用窗口通告"还能再收多少",发送方据此决定"还能再发多少"。

成长:传数据与滑动窗口

真实账本片段——一次 100 字节的请求与响应:

$ tshark -r conn.pcapng -Y "tcp.stream==0 && tcp.len>0" -T fields \ -e frame.number -e tcp.srcport -e tcp.seq -e tcp.len -e tcp.ack 8 52114 1 100 - 客户端发 100 字节(相对序号 1 起) 9 443 1 1460 101 服务端响应首段,同时确认了客户端的 101 10 443 1461 1460 101 续传 11 52114 101 0 2921 客户端纯 ACK:你的 2920 字节收齐了

第 11 帧是教科书式的确认:客户端自己没有数据要发(长度 0),确认号 2921 表示对端 1 到 2920 的字节全部收齐。推演口诀:确认号等于对方序号加对方长度;纯 ACK 的长度是 0,不占序号。

变故:账本对不上的时候

网络丢包时,TCP 靠重传恢复,账本会留下痕迹。两类高频痕迹:

# 重传:同一序号的段再次出现 $ tshark -r conn.pcapng -Y "tcp.analysis.retransmission" -T fields \ -e frame.number -e tcp.seq -e tcp.len -e tcp.analysis.rto 42 5881 1460 0.212000 <- 距上次发送 212 毫秒后重传 # 快速重传:收到三个重复确认后不等超时 $ tshark -r conn.pcapng -Y "tcp.analysis.fast_retransmission" -c 1 47 10.881 192.168.31.14 → 203.0.113.25 TCP 1502 [TCP Fast Retransmission] seq=5881

tcp.analysis 系列字段是 TCP 解析器记账的产物(3.3 节"解剖刀有状态"的兑现):重传、乱序、重复确认、零窗口探测,全部是账本对账对出来的结论。第 8 章会把它们组织成完整的判定模式。

闭幕:挥手与两种结局

正常闭幕是四次挥手(FIN 与 ACK 交错),异常闭幕是一个 RST。抓包判定要点:

# 正常挥手:双方各发一个 FIN,各自被确认 $ tshark -r conn.pcapng -Y "tcp.flags.fin==1" -T fields -e frame.number -e tcp.srcport -e tcp.seq -e tcp.ack 88 52114 201 3550 90 443 3550 202 # RST 三大常见成因的现场区分 RST 且前一帧是 SYN → 端口没开(服务未启动) RST 出现在数据流中间 → 进程崩溃或被强杀 RST 且同时收到 ICMP → 中间设备在替服务端拒绝(防火墙重置)

FIN 与 SYN 一样各占一个序号,所以对 FIN 的确认号也要加一。挥手阶段抓不全(只抓到一半 FIN)会让连接在解析器眼里"未闭账",Follow Stream 仍能拼出完整数据——流重组不依赖挥手。

⚠️ 一个高频误判:看到 RST 就断言"被防火墙拦了"。先看 RST 的前一帧:是 SYN(端口未开放)还是数据中途(进程问题),再看有没有伴随 ICMP。三步走完再下结论,RST 的锅才能扣对。

账本推演演练:一段完整交互的手工对账

拿一段四帧交互,手工推演账本再对答案(相对序号,Wireshark 默认口径):

帧 seq len ack 说明 21 1 400 - 客户端发 400 字节 22 1 1460 - 服务端开始回 1460 字节 23 1461 1460 - 服务端续传 24 401 0 2921 客户端确认

推演过程:帧 24 的确认号 2921 从哪来——服务端已发 seq 1 至 2920(两段各加:1+1460=1461 起,1461+1460=2921 止),所以"收齐到 2920,期待 2921"。客户端自己的序号走到 401(400 字节 + 起始 1)。用命令验证:

$ tshark -r trade.pcapng -Y "frame.number in {21 22 23 24}" -T fields \ -e tcp.seq -e tcp.len -e tcp.ack 1 400 0 1 1460 0 1461 1460 0 401 0 2921 <- 与手工推演逐位吻合

对账功力决定你能否回答这类追问:"客户端到底有没有收到服务端的第二段?"——答案就藏在帧 24 的确认号里(2921 > 1461,两段都确认了)。排障时最值钱的往往不是看到哪帧丢了,而是能用账本证明"哪帧确实被收到过"

结案要点

  • 窗口缩放只在 SYN 里出现:排障要抓全握手,否则真实窗口永远推不出。
  • 确认号推演口诀:对方序号加对方长度;SYN 与 FIN 各占一个虚拟字节。
  • tcp.analysis 系列是账本结论:重传、乱序、零窗口的判定依据全在这里。
  • 零窗口是"对端收不动",不是"网络丢":方向完全不同的两类病,第 8 章细辨。
  • RST 三步归因:看前一帧、看时机、看 ICMP 伴随。

传输层账本建立完毕,下一节进入应用层——DNS、HTTP、TLS 三个高频案宗的报文解剖。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U