最近在整理网络基础相关的学习笔记,把 TCP、抓包、TIME_WAIT 和防火墙这四块内容串在一起做了个复盘。说实话,单独看每个知识点都不难,但实际排障的时候它们往往是一起出现的:客户端报连接超时,你第一反应是抓包确认三次握手是否完成;握手包发出去了没有应答,你又要去翻防火墙策略;调完防火墙问题确实缓解了,过两天又冒出来大量 TIME_WAIT 导致端口不够用。这篇文章就是我这次从原理到实操的完整记录,适合刚接触网络排查的运维、开发同学,也适合想系统补一遍 TCP 状态、抓包分析思路的老朋友。我会结合真实的抓包截图场景(用文字还原)、命令输出和配置案例,把每一步为什么这样做讲清楚。
1. TCP三次握手:抓包才看得见的“三次”
1.1 三次握手里的“语义交换”
TCP 三次握手这件事,教科书上讲的是 SYN、SYN-ACK、ACK 三个报文,但我在实际抓包之前,其实一直没真正理解为什么必须是这三个报文,缺一个会怎样。后来自己用 Wireshark 抓了一次本地回环报文,才把这里面的逻辑看透。
三次握手表面上是“建立连接”,本质上是在交换三个关键信息:双方的初始序列号(ISN)、双方的接收窗口大小、以及双方是否愿意收发数据。
- 第一次握手:客户端发送 SYN,携带客户端初始序列号 client_isn,同时告诉服务器“我想建立连接”。
- 第二次握手:服务器回复 SYN-ACK,携带 server_isn,同时确认客户端的 client_isn(ACK = client_isn + 1),表示“收到你的请求,我也愿意建立连接”。
- 第三次握手:客户端回复 ACK,确认服务器的 server_isn,表示“我也收到你的确认了”。
为什么第三次是必需的?因为如果不发这个 ACK,服务器无法确认客户端是否收到了自己的 SYN-ACK。如果服务器误以为客户端收到了,它就会进入 ESTABLISHED 状态,然后开始给客户端发送数据,但客户端其实还在 SYN_SENT 状态,两边状态对不上,数据就会乱套。
这里有个细节我一开始忽略了:序列号不是从 0 开始的,操作系统会用一套算法生成一个随机初始序列号。抓包时看到的 seq 值往往很大,比如 4234567890,这是正常的。三次握手完成之后,双方就以这两个序列号作为起点,开始为后续每个字节编号。
1.2 用 Wireshark 实际抓一次握手
我在自己电脑上用 Wireshark 抓本地回环(loopback)流量,命令是启动一个简单的 HTTP 服务,然后用 curl 访问它。抓包时先设置过滤条件,只关注 TCP 流量:
code复制tcp.port == 8080
把 HTTP 服务跑起来后,在 Wireshark 里就能看到一系列 TCP 报文。最前面三个就是三次握手:
code复制Frame 1: 客户端 → 服务端 [SYN] Seq=0 Win=65535
Frame 2: 服务端 → 客户端 [SYN, ACK] Seq=0 Ack=1 Win=65535
Frame 3: 客户端 → 服务端 [ACK] Seq=1 Ack=1 Win=65535
注意这里的 Seq=0 是 Wireshark 做了“相对序列号”显示优化,实际线上的绝对序列号不是 0。如果你单击一个 TCP 报文,在中间面板展开 Transmission Control Protocol 字段,能看到 Sequence Number (raw) 才是真实值。这个显示优化默认开启,如果想关掉,在 Wireshark 的 Preferences → Protocols → TCP 里取消 Relative sequence numbers 即可。
过滤三次握手有一种更精确的写法:
code复制tcp.flags.syn == 1
这个表达式会把 SYN 和 SYN-ACK 都过滤出来,因为这两个报文的 SYN 位都是 1。再配合 tcp.flags.ack == 0,就能只看第一次握手的纯 SYN 包:
code复制tcp.flags.syn == 1 && tcp.flags.ack == 0
通过握手时延可以初步判断网络质量。第一次握手到第二次握手的间隔是 RTT(往返时延),本地回环通常是 0.1 毫秒以内;如果是跨机房、跨公网,这个值会明显变大,甚至上百毫秒。我在排查慢请求问题时,第一步就是看这个间隔,如果 RTT 本身就很高,那基本跟应用代码没关系,是网络链路的问题。
1.3 握手失败时的典型异常报文
抓包最核心的价值不是看正常的握手过程,而是看失败时的报文形态。常见的几种异常:
第一,SYN 重传。客户端发出 SYN 后,如果一直没有收到 SYN-ACK,会隔一段时间重新发送 SYN,间隔指数退避(1 秒、2 秒、4 秒……),重传超过一定次数才放弃。抓包的时候会看到同一个源端口、同一个序列号的 SYN 反复出现,这时候首选的怀疑对象就是中间链路或防火墙把 SYN 包丢了,或者服务端根本没有进程监听该端口。
第二,RST 直接拒绝。如果服务端收到了 SYN,但对应端口没有进程监听,内核会直接回一个 RST 报文把连接打断。客户端的表现是 connection refused,秒失败,不需要等超时。这个现象其实可以帮我们快速区分问题类别:秒拒通常是端口没监听或者防火墙主动 RST;久久没反应才是包被静默丢弃。
第三,TCP Keep-Alive 导致的半开连接探测。在实际运维中,还会在抓包里看到其实是连接已经建立后,某一端因为长时间没数据通信,主动发送 Keep-Alive 探测包。但这已经超出握手范畴,放到后面的状态分析里再说。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
