1. 先说清楚:TCP头部为什么值得你花时间逐字节看
很多做网络开发或者运维的朋友都有过这种经历:抓包工具里明明能看到完整的TCP报文,但一列字段展开在眼前,除了源端口、目的端口认识,剩下的序号、确认号、窗口大小、标志位,全是一知半解。面试的时候被问到TCP头部有哪些字段,能背出“20字节固定头部”这个数字,但真让说说每个字段在什么场景下起作用,就卡壳了。
这篇文章我就从实际抓包和排障的角度,把TCP头部彻底拆开讲一遍。核心关键词就是TCP头部本身——这20字节(不含选项)里承载了TCP协议的全部核心机制:可靠传输靠序号和确认号,连接管理靠标志位,流量控制靠窗口字段,差错检测靠校验和。读懂了TCP头部,你也就真正读懂了TCP协议的设计思想。
这篇内容适合这几类人:刚入门准备面试的后端开发、需要定位网络故障的运维工程师、做嵌入式网络开发需要调协议栈的硬件工程师,以及所有想从“会用TCP”进阶到“懂TCP”的人。看完之后,你再打开Wireshark,看到的就不是一串十六进制,而是一套有逻辑的会话记录。
2. 先建立整体认知:TCP头部在报文里的位置和数据组织方式
2.1 一个TCP报文段的完整组成
先明确一个概念:TCP头部不是一个独立存在的东西,它是TCP报文段的一部分。一个完整的TCP报文段长这样:
| 以太网头部 | IP头部 | TCP头部 | 应用数据 |以太网头部通常14字节,IP头部普遍是20字节(没有选项时),TCP头部最少20字节。链路层MTU如果是1500,那IP层最多承载1500-14=1486字节,扣除IP头部20字节后,TCP报文段最大是1466字节,再扣掉TCP头部至少20字节,应用层一次最多能塞进1446字节数据(这里没有考虑TCP选项,实际带时间戳等选项时还要再扣)。
这个过程里经常有人搞混“MSS”和“MTU”。简单说,MSS是TCP层能承载的最大应用数据字节数,它等于MTU减去IP头部和TCP头部的固定开销。这个值会在TCP握手的时候通过选项字段协商,而不是谁硬性规定的。
2.2 TCP头部的固定部分和可变部分
TCP头部的最小长度是20字节,这20字节的布局如下:
| 偏移(字节) | 字段 | 长度 | 作用 |
|---|---|---|---|
| 0-1 | 源端口 | 16位 | 标识发送方应用进程 |
| 2-3 | 目的端口 | 16位 | 标识接收方应用进程 |
| 4-7 | 序号(Sequence Number) | 32位 | 本报文段第一个字节在数据流中的位置 |
| 8-11 | 确认号(Acknowledgment Number) | 32位 | 期望收到对方下一个字节的序号 |
| 12 | 数据偏移 + 保留 | 4位+4位(保留3位+NS标记1位) | 指示TCP头部长度 |
| 13 | 标志位 | 8位 | CWR、ECE、URG、ACK、PSH、RST、SYN、FIN |
| 14-15 | 窗口大小 | 16位 | 声明本端还能接收多少字节 |
| 16-17 | 校验和 | 16位 | 覆盖TCP头部和数据(含伪头部)的校验 |
| 18-19 | 紧急指针 | 16位 | 配合URG标志,指明紧急数据末尾位置 |
在这20字节之外,还有可变长的“选项”部分,长度由数据偏移字段指定。最常见的选项包括MSS、窗口缩放因子、时间戳、SACK(选择性确认)等。这些不是可有可无的装饰,TCP的传输效率、大带宽场景下的正确性,很大程度上都靠这些选项撑着。
2.3 端口号只有16位意味着什么
源端口和目的端口各占16位,取值范围是0-65535。0-1023是知名端口,1024-49151是注册端口,49152-65535是动态/私有端口。16位端口号加上32位IP地址,构成了一个四元组(源IP、源端口、目的IP、目的端口),这个四元组唯一标识一条TCP连接。
实际排障时经常遇到端口耗尽的问题。比如一台服务器作为客户端大量向外发起连接,可用端口范围默认是32768-60999(Linux)或者49152-65535(Windows),如果TIME_WAIT状态的连接太多占着端口不释放,新连接就会因为“Cannot assign requested address”而失败。这个问题的根源就在这16位的端口号空间上——即使内存、CPU都够,端口号用完了就是连不上。理解了头部的这个限制,你自然就会想到调整net.ipv4.ip_local_port_range、开启tcp_tw_reuse或者改造连接池策略。
3. 20字节固定头部里的每个字段,到底在干什么
3.1 序号和确认号:TCP可靠性最底层的骨架
序号(Sequence Number)占32位,范围是0到2^32-1,超出后回绕。它表示本报文段中第一个数据字节在整个字节流中的位置。举个例子:一次连接建立后,初始序号(ISN)不是0,而是一个随机数。如果ISN是1000,第一个数据报文段携带100字节数据,那这个段的序号就是1000,下一个段的序号就是1100。
这里有一个关键点:序号针对的是字节,不是报文段。TCP是面向字节流的协议,它不管应用层把数据切成多少块,只看字节顺序。所以哪怕应用层write了两次,协议栈可能合并成一个段发送,也可能把一个大数据拆成几个段发,接收方全部靠序号把字节流重组回原始顺序。
确认号(Acknowledgment Number)表示“我期望收到的下一个字节序号”。也就是说,确认号N意味着序号N之前的所有字节都已经被正确接收。TCP使用累积确认机制——它不是每个段都回一个ACK,而是确认到某个字节为止,之前的所有字节都算收到。
实际抓包中最常见的问题就是看TCP重传:如果同一个序号反复出现在抓包里,说明接收方一直没有发回包含更高确认号的ACK,发送方等了超时时间后只能重传。有一次我排查一个跨机房同步延迟高的故障,抓包看到大量DUP ACK和重传,点开头部一看,接收方通告的窗口一直是0,原因就是接收方应用层处理不过来,缓冲区满了。这直接引出了后面要讲的窗口字段。
3.2 数据偏移和保留位:这4位决定了TCP头部到底多长
数据偏移字段占4位,单位是4字节。它表示TCP头部有多少个32位字。为什么要这个字段?因为TCP头部有可变长的选项部分。如果数据偏移值是5,说明头部是5×4=20字节,即没有选项;如果值是15,说明头部是60字节,这是TCP头部的上限。
这里容易踩坑的常见问题是“TCP头部最大60字节 vs 固定20字节”的说法到底哪个对。固定20字节是最小值,60字节是最大值,两者并不矛盾。选项字段塞太多会导致头部膨胀,留给应用数据的空间变小,但正常通信中选项一般就12-40字节。
保留位有3位,另外还有1位NS标记(Nonce Sum),是显式拥塞通知(ECN)机制的一部分。正常情况下这三四位都是0,抓包看到它们有值,说明启用了ECN相关的扩展功能。
3.3 标志位:TCP连接状态机的开关面板
标志位共8位,这8个开关控制了TCP的所有行为状态。我平时排障几乎每天都在跟它们打交道:
- SYN:同步标志。连接建立时,发起方发SYN=1的报文段;接收方回复SYN+ACK。
- ACK:确认标志。表示确认号字段有效。除了最初的SYN包,后续所有报文段都必须置ACK。
- FIN:结束标志。一方发送FIN=1表示“我的数据发完了,想关闭连接”。
- RST:重置标志。出现异常时用于强制断开连接。比如访问一个不存在的端口,对端会回RST。
- PSH:推送标志。表示接收方应该尽快把数据交给应用层,而不是等缓冲区满了再交付。实际上现代协议栈对PSH的处理没那么严格,很多实现根本忽略它。
- URG:紧急标志。配合紧急指针使用,标记数据中有紧急内容需要优先处理。现实中极少遇到,因为应用层有更好的方式处理优先级数据。
- ECE和CWR:ECN相关的拥塞通知标志。ECE用于通告网络拥塞,CWR用于表示发送方已降低发送速率。这两个在局域网抓包基本见不到,但在跨运营商的大流量传输中可能出现。
在分析TCP头部时,标志位组合是最需要读懂的信息。三次握手的SYN→SYN+ACK→ACK,四次挥手的FIN→ACK→FIN→ACK,都是标志位的组合变化。RST包的出现则几乎一定是异常信号:后端服务没监听、防火墙丢包后重置、程序主动关闭未读完的连接,都可能导致RST。
3.4 窗口大小:告诉对端“你现在能发多少”
窗口大小字段占16位,单位是字节。它表示发送方本端还能接收多少数据,也就是接收窗口的剩余空间。这个字段是TCP流量控制的核心:接收方通过不断更新窗口大小,告诉发送方“我现在缓冲区还剩X字节,你最多发X字节,别多塞”。
16位最大只能表示65535字节,这在现代高带宽网络下远远不够。所以TCP引入了窗口缩放因子(Window Scale)选项,通过左移操作把窗口字段实际表示的值放大到最大1GB。这个选项只能在SYN包里协商,连接建立后就不能再变了。曾经排查过一个存储同步慢的问题,两台服务器的TCP窗口缩放没协商成功(老版本系统默认关闭),实际吞吐只有几十MB/s,打开窗口缩放后直接跑满万兆网卡。这就是头部字段直接影响业务性能的典型例子。
3.5 校验和:TCP检测报文损坏的最后防线
校验和字段覆盖三部分内容:TCP头部、TCP数据、以及一个12字节的伪头部(Pseudo Header)。伪头部包含源IP、目的IP、协议号(TCP是6)和TCP长度,它不随报文传输,只是参与校验计算。
为什么要凑上IP地址这些内容?因为TCP校验和不光要检测数据在传输中是否被改坏,还要防止IP层把报文投递错了地方。如果源IP或目的IP不对,校验和会失败,接收方直接丢弃这个报文段。
校验和计算是反码求和,发送方先填0计算,接收方把整个段连同校验和一起做反码求和,结果全为1才是正确的。抓包工具如果显示“checksum incorrect”,通常不是报文真坏了,更多是硬件校验和卸载(checksum offload)导致抓包软件看到的校验和是错的——网卡在发送前才填上正确值,抓包是在填之前抓的。这个坑我见不少人踩过,看到校验和错误就以为链路丢包严重,实际上只要关掉Wireshark里的校验和校验或者关掉网卡的checksum offload再看,报文是好的。
3.6 紧急指针:一个大多数场景用不到的字段
紧急指针占16位,它只有URG标志置1时才有意义。它是一个正偏移量,加上序号字段就是紧急数据的最后一个字节的位置。TCP紧急数据的本意是让接收方快速处理带外数据,比如交互式终端里的Ctrl+C中断信号。但实际工程中,几乎所有应用都用普通数据的特殊字节来模拟紧急信号,URG机制反而因为实现复杂、各系统支持不一致,很少被使用。你抓一万个包可能都见不到一次URG=1的报文。
4. 从TCP头部看三次握手与四次挥手:连接状态的变化全写在标志位里
4.1 三次握手:SYN和ACK如何一步步确认双方能力
经典的三次握手过程,用TCP头部的字段来解读会特别清晰:
- 客户端发送SYN报文:SYN=1,序号字段填入客户端的初始序号(比如1000),确认号无效(ACK=0),不携带数据。
- 服务器收到后回复SYN+ACK报文:SYN=1,ACK=1,序号字段填入服务器的初始序号(比如5000),确认号字段填入客户端初始序号+1(即1001)。
- 客户端再回复ACK报文:SYN=0,ACK=1,确认号填入服务器初始序号+1(即5001)。
注意第三步的序号已经是1001了(因为第一步虽然没带数据,但SYN包本身也消耗一个序号)。TCP规定SYN和FIN标志位都各自占用一个序号,这是个容易出错的知识点。分析抓包时,如果发现确认号没有按“对方初始序号+1”来递进,握手一定有问题。
抓包时我习惯看的是握手的第四个字段组合:第一次握手包有没有带MSS选项、窗口缩放选项、时间戳选项。如果服务器回复的SYN+ACK里没有MSS选项,说明服务器可能使用了很老的协议栈,或者中间设备改写了报文,这会严重影响后续大包传输。
4.2 为什么是三次而不是两次
这个问题的答案也在TCP头部里。客户端发的SYN可能会因为网络延迟被重传,如果只有两次握手,服务器收到一个迟到的SYN就会建立一条没人用的连接,白白分配资源。三次握手里,客户端收到服务器的SYN+ACK后,如果发现自己没有发起过连接,就会回一个RST报文告诉服务器“我不需要这条连接”,服务器收到RST后丢弃半开连接。RST标志在这个场景下就是清理僵尸连接的开关。
另外一个角度看,三次握手让双方各自确认了“我能收到你的包,你也能收到我的包”。客户端收到SYN+ACK,证明客户端的SYN到达了服务器,服务器的响应也回到了客户端;服务器收到最后的ACK,证明服务器的SYN+ACK到达了客户端。两次握手做不到双方互相确认。
4.3 四次挥手:FIN和ACK的分工与TIME_WAIT的由来
四次挥手的过程相对复杂一些:
- 主动关闭方发送FIN报文:FIN=1,表示“我的数据发完了”。
- 被动关闭方回复ACK:确认号填FIN的序号+1,表示“我收到了你的FIN,但我还有数据要发”。
- 被动关闭方数据发完后,发送FIN报文:FIN=1。
- 主动关闭方回复ACK。
这里有个细节值得展开:被动关闭方回复ACK后,TCP连接进入了CLOSE_WAIT状态,应用层要继续处理完剩余逻辑才能调用close。排障时经常看到服务器上大量CLOSE_WAIT连接,点开抓包就会发现是应用代码没关闭socket——三次握手之后的某个位置查出来,都是业务代码的问题,不是协议栈的问题。
主动关闭方在发送最后一个ACK后会进入TIME_WAIT状态,持续2MSL(Maximum Segment Lifetime)。为什么需要这个状态?TCP头部的序号回绕问题在这里也有体现:如果最后一个ACK丢了,被动关闭方会重发FIN,主动关闭方必须保留足够的状态来处理重发的FIN。2MSL确保网络中所有旧报文段都已消亡,不会干扰到新连接。这也是为什么大量短连接场景下TIME_WAIT堆积成灾,因为每个主动关闭的连接都要在TIME_WAIT里待满60秒。
4.4 异常断开的RST标志:哪些情况会看到它
RST标志一出现,基本等于TCP层在说“我不玩了,立刻断”。常见场景包括:
- 向一个没有监听端口的IP发SYN,对端返回RST(端口不可达)。
- 连接建立后,一方长时间没收到数据,应用层超时主动调用SO_LINGER强制关闭,发送RST。
- 防火墙等中间设备发现连接状态不一致,发送RST中断。
- 服务器accept队列满了,内核直接对新的SYN返回RST。
排查RST包时,有一种情况很容易忽视:中间设备(比如负载均衡器)替代服务器发送RST。此时抓包看到RST的源IP是负载均衡器,而不是真实服务器,如果你只看服务器网卡抓包,根本找不到RST的来源。这也是我养成了“客户端本机抓包+服务器本机抓包+中间设备抓包”三方对照习惯的原因。
5. 选项字段:TCP性能的秘密藏在20字节之外
5.1 MSS选项:协商一个双方都舒服的最大段大小
MSS(Maximum Segment Size)选项只能在SYN报文中携带。它表示发送方本端愿意接收的单个TCP报文段中最大应用数据大小。默认值通常由MTU推导:MTU 1500减去IP头部20字节减去TCP头部20字节,得到1460字节。
协商原则是取双方的较小值。局域网内MTU一般是1500,MSS就是1460;PPPoE拨号环境下MTU是1492,MSS就是1452。注意一个经典问题:如果握手完成后,中间链路的MTU变小了,而双方没有启用PMTUD(路径MTU发现)或者ICMP被防火墙过滤,就会出现“大包发不出去、小包正常”的现象,表现就是网页打不开、SSH卡顿。这种问题在TCP头部里看MSS字段能快速判断:如果MSS显示1460,但中间实际链路MTU只有1400,需要手动调小接口MTU或者用防火墙改MSS。
5.2 窗口缩放选项:突破16位窗口上限的关键
前面说了窗口字段只有16位,最大65535字节。在时延高的链路上,这个窗口大小会严重限制吞吐。计算带宽时延积(BDP)的公式是:带宽(bps)× 往返时延(秒)。比如一条100Mbps链路,RTT是100ms,BDP = 100Mbps × 0.1s = 10Mbit ≈ 1.25MB。如果窗口只有64KB,单条TCP连接最多飞到512Kbps左右,根本跑不满带宽。
窗口缩放选项解决的就是这个问题。它在SYN里通告一个缩放因子,比如因子是7,意味着窗口字段左移7位,实际窗口 = 窗口字段值 × 2^7。最大可以表示窗口值65535×2^14,接近1GB。
但这个选项只在SYN中协商,连接建立后无法修改。所以排查大流量传输性能问题时,一定要先确认握手的SYN包里双方都通告了窗口缩放,而且最好在服务器上配置net.ipv4.tcp_window_scaling=1确保开启。如果有一方不支持,整个连接期间窗口上限就是64KB,性能直接腰斩。
5.3 时间戳选项:解决序号回绕和精确测量RTT
时间戳选项占10字节,存放在TCP选项中。它的作用主要有两个:
一是精确计算RTT。发送方在报文里记录发送时间戳,接收方在ACK里原样返回这个时间戳,发送方就能算出准确的往返时间,比靠数据重发计时更精确。这对于拥塞控制算法的决策很重要。
二是防止序号回绕导致的旧包误判。32位序号在高速传输下很快会绕回0,比如10Gbps链路,大约每3.4秒绕完一圈。如果没有时间戳,接收方可能分不清一个序号较小的包到底是新包还是旧包重传。有了时间戳,接收方可以通过时间戳递增规则判断新旧。
注意:时间戳选项也必须在握手时协商。有些安全扫描工具会通过时间戳推测系统运行时间,所以部分系统会关闭时间戳。关闭后,大流量下的序号回绕保护和RTT精度会受一定影响。
5.4 SACK选项:面对乱序和丢包时的选择性确认
标准TCP的累积确认有个明显弱点:如果接收方收到了序号1000-2000的数据,但中间丢了1500-2000的分片,它只能确认到1500,发送方就得把1500之后的所有数据全部重传,哪怕接收方已经收到了其中大部分。
SACK(Selective Acknowledgment)选项解决了这个问题。接收方在ACK中夹带SACK块,告诉发送方“我虽然只能确认到1500,但我其实已经收到了2000-2500这段”。发送方看到SACK信息后,只重传真正丢失的段,节省大量带宽。
Wireshark抓包时看到ACK包里有SACK字段,就说明连接启用了选择性确认。老一点的协议栈或者中间设备如果禁用了SACK,大带宽高丢包链路上重传效率会很差。还有一个常见坑:某些TCP栈的SACK实现有安全问题(历史上曾经曝出恶意SACK导致CPU耗尽),但现代内核版本已经修复,不需要为了安全关闭SACK而牺牲性能。
6. Wireshark实战:从TCP头部字段定位网络故障
6.1 握手阶段:一眼看出连接失败卡在哪一步
三层握手的抓包分析是最基础也是最常用的实战技能。打开Wireshark,过滤tcp.flags.syn==1,看到的第一个SYN包如果始终没有对应SYN+ACK返回,而且源IP不断重传SYN,说明SYN包要么没到达对端,要么对端的SYN+ACK回不来。这时候我会依次看三件事:SYN包的目的端口有没有写错、中间防火墙有没有丢包、对端服务器有没有在监听这个端口。
如果收到了SYN+ACK,但客户端一直不回复最后的ACK,说明客户端在收到SYN+ACK之后卡住了。可能是客户端本地防火墙规则拦了入站包,也可能是客户端socket已经超时关闭。点开SYN+ACK的TCP头部,确认序号和确认号是否正确,如果确认号不是客户端SYN的序号+1,说明对端回复的是一个异常包。
6.2 传输阶段:重传和零窗口的头部特征
传输阶段最常见的两个问题是TCP重传和零窗口。
TCP重传在抓包里很好认:同一个序号反复出现,而且时间间隔呈指数退避。看头部字段时,注意重传包的确认号是否在增长。如果确认号一直不变,说明对端根本没收到新数据,或收到了但没回ACK。这时候要区分是丢包还是接收端处理能力问题——检查接收端通告的窗口大小,如果窗口字段一直显示0,就是接收端缓冲区耗尽,属于应用层消费慢的问题;如果窗口不为0但确认号不增长,才更可能是链路丢包。
零窗口(Zero Window)现象我遇到过很多次。TCP头部里窗口字段显示0,相当于接收端在喊“暂停,我撑不住了”。发送端收到零窗口后,会停止发送数据并启动持续计时器,周期性发送一字节的窗口探测包(Window Probe),探测接收端窗口是否恢复。抓包时看到零窗口报文,不要急着调TCP参数,先查接收端应用为什么不消费数据:是不是线程池满了?数据库连接池满了?死锁了?应用层恢复,窗口自然就恢复了。
6.3 关闭阶段:TIME_WAIT和CLOSE_WAIT的头部观察
用tcp.flags.fin==1过滤,能看到所有发起关闭的报文。正常四次挥手里,主动关闭方发FIN、被动方回ACK,被动方再发FIN、主动方回ACK。如果只看到一方反复发FIN而不见ACK,说明对端不响应关闭请求——大概率是应用层进程挂死,内核没机会处理socket关闭。
服务器上ss -s看到大量TIME_WAIT连接时,抓包能看到每个连接的最后ACK都是同一个客户端IP发出的。如果客户端是NAT网关后面的多台机器,那TIME_WAIT会集中在网关出口IP上。对于高并发短连接服务,可以通过调整net.ipv4.tcp_tw_reuse、net.ipv4.tcp_fin_timeout来缓解,但前提是你理解了TIME_WAIT本身是TCP可靠性的保障——直接全关会引入旧包串扰的风险。
6.4 一个综合排查案例:头部字段如何串联起整个问题链
有次线上服务反馈“偶尔连接超时”,我同时抓客户端和服务器的包。客户端这边看到的完整过程是:SYN重传了3次后放弃,报connection timed out。服务器那边却完全没有收到任何SYN包。这说明SYN在中间链路就被丢了。继续在接入交换机上抓包,找到防火墙策略日志,发现某条安全组规则把客户端IP所在网段的入方向SYN包丢弃了,但其他协议放行,所以普通ping测试看不出来。在这个排查过程里,TCP头部的SYN标志、重传序号、端口号字段是定位链路层问题的关键线索——没有头部字段的精确标识,你很难从海量流量里锁定到具体是哪条流的哪个状态出了问题。
这个案例给一个方法论上的启发:TCP头部信息要和中间设备日志配合看,而不是只看一个点。头部字段告诉你“连接在哪个状态停止推进”,中间设备日志告诉你“哪个环节把它拦了”。两者对上了,问题才算真正定位。
7. 一些容易踩的细节坑和我的习惯性做法
TCP头部看起来简单,实际工程里细节非常多。我把自己长期踩坑总结出来的几条经验列在这里,这些在标准文档里不会写得太细,但实战价值很高。
第一,抓包时先确认校验和卸载的影响。无论是Linux还是Windows,现代网卡默认开启checksum offload,Wireshark会报很多checksum incorrect。看到这类红色标记不要慌,先到Wireshark里把“Validate checksums if possible”关掉,或者对比发端和收端是否有相同的不正确校验和计数。如果两端都报同样的校验和错误,才需要考虑链路质量问题。
第二,分析TCP头部时别忘了IP头部的配合。TCP头部里的序号和确认号是针对字节流的,但IP头部里的Identification字段是针对IP分片的。两者含义完全不同。怀疑分片问题时,一定要看IP头部的Flags和Fragment Offset,TCP头部本身没有分片相关信息。很多人拿着TCP头部找MTU问题的证据,找半天找不到,就是因为分片信息在IP层。
第三,注意SYN包是否携带了MSS、窗口缩放、时间戳选项。三个选项同时出现是现代TCP协议栈的正常行为。如果SYN包里只有MSS没有时间戳,或者没有窗口缩放,多半是中间设备做了TCP选项改写,或者对端是非常老的系统。这种“看起来能建立连接但性能不对”的故障,在建立连接那一刻就已经埋下隐患了。
第四,RST包和FIN包的处理完全不同。RST是异常终止,它不经过TIME_WAIT,直接释放所有连接资源;FIN是正常关闭,要完整走完四次挥手。写代码的时候,如果发现异常但直接close掉socket,内核默认发送的是FIN而不是RST,对端还会傻等数据,可能引发下一次发送时触发RST。想快速终止连接,应该设置SO_LINGER并将l_linger设为0,这样close时内核会发RST。
第五,TCP头部序号和确认号的换算没那么难,但经常被忽略。Wireshark里默认显示的是相对序号(Relative Sequence Number),也就是从0开始递增的数字。相对序号对阅读友好,但排查和外部系统对接的问题时,可能需要切回绝对序号(Absolute Sequence Number)。右键点击序号列,选择“Protocol Preferences”里的相对序号开关,切换着看能帮你避免“两边抓包序号对不上”的困惑。
关于TCP头部,能写的内容其实还有很多,比如ECN的显式拥塞通知如何与头部标志位协同、紧急指针在Telnet等古老协议里的实际用法、SYN Cookie如何影响握手阶段的序号生成规则等等。但说到底,TCP头部的每个字段都不是孤立存在的,它们组合在一起,支撑起了TCP作为互联网基础协议的四大核心能力:可靠传输、流量控制、拥塞控制、连接管理。把这20字节吃透了,你排查网络问题的时候会多一整套分析框架,而不是只停留在“通不通”“通不通”的层面。我自己的体会是,每次觉得TCP协议复杂到不想深究的时候,就打开抓包文件盯着头部字段看一会儿,比看十篇理论文章都管用。