TCP 序列号和确认号是如何变化的?
大家好,我是小林。
在网站上回答了很多人的问题,我发现很多人对 TCP 序列号和确认号的变化都是懵懵懂懂的,只知道三次握手和四次挥手过程中,ACK 报文中确认号要 +1,然后数据传输中 TCP 序列号和确认号的变化就不知道了。
也有很多同学跟我反馈,希望我写一篇关于 TCP 序列号和确认号变化过程的文章。大家别小看这个基础知识点,其实很多人都不知道的。
所以,这次就跟大家聊聊以下过程中,TCP 序列号和确认号是如何变化的?
- 三次握手中 TCP 序列号和确认号的变化
- 数据传输中 TCP 序列号和确认号的变化
- 四次挥手中 TCP 序列号和确认号的变化
万能公式
我根据经验总结了一条万能公式。
发送的 TCP 报文:
- 公式一:序列号 = 上一次发送的序列号 + len(数据长度)。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为 上一次发送的序列号 + 1。
- 公式二:确认号 = 上一次收到的报文中的序列号 + len(数据长度)。特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为上一次收到的报文中的序列号 + 1。
可能有点抽象,接下来举一些实际的场景,加深对这个万能公式的理解。
先给大家看看 TCP 序列号和确认号在 TCP 头部的哪个位置。可以看到,这两个字段都是 32 位。
在这里插入图片描述
这里重点关注这三个字段的作用:
- 序列号:在建立连接时由内核生成的随机数作为其初始值,通过 SYN 报文传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
- 确认号:指下一次「期望」收到的数据的序列号,发送端收到接收方发来的 ACK 确认报文以后,就可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
- 控制位:用来标识 TCP 报文是什么类型的报文,比如是 SYN 报文、数据报文、ACK 报文,FIN 报文等。
三次握手阶段的变化
先来说说三次握手中 TCP 序列号和确认号的变化。
假设客户端的初始化序列号为 client_isn,服务端的初始化序列号为 server_isn,TCP 三次握手的流程如下:
在这里插入图片描述
在这里我们重点关注,下面这两个过程。
服务端收到客户端的 SYN 报文后,会将 SYN-ACK 报文(第二次握手报文)中序列号和确认号分别设置为:
- 序列号设置为服务端随机初始化的序列号 server_isn。
- 确认号设置为 client_isn + 1,服务端上一次收到的报文是客户端发来的 SYN 报文,该报文的 seq = client_isn,那么根据公式 2(_确认号 = 上一次收到的报文中的序列号 + len。特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为 + 1_),可以得出当前确认号 = client_isn + 1。
客户端收到服务端的 SYN-ACK 报文后,会将 ACK 报文(第三次握手报文)中序列号和确认号分别设置为:
- 序列号设置为 client_isn + 1。客户端上一次发送报文是 SYN 报文,SYN 的序列号为 client_isn,根据公式 1(_序列号 = 上一次发送的序列号 + len。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为 + 1_),所以当前的序列号为 client_isn + 1。
- 确认号设置为 server_isn + 1,客户端上一次收到的报文是服务端发来的 SYN-ACK 报文,该报文的 seq = server_isn,那么根据公式 2(_确认号 = 收到的报文中的序列号 + len。特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为 + 1_),可以得出当前确认号 = server_isn + 1。
为什么第二次和第三次握手报文中的确认号是将对方的序列号 + 1 后作为确认号呢?
SYN 报文是特殊的 TCP 报文,用于建立连接时使用,虽然 SYN 报文不携带用户数据,但是 TCP 将 SYN 报文视为 1 字节的数据,当对方收到了 SYN 报文后,在回复 ACK 报文时,就需要将 ACK 报文中的确认号设置为 SYN 的序列号 + 1 ,这样做是有两个目的:
- 告诉对方,我方已经收到 SYN 报文。
- 告诉对方,我方下一次「期望」收到的报文的序列号为此确认号,比如客户端与服务端完成三次握手之后,服务端接下来期望收到的是序列号为 client_isn + 1 的 TCP 数据报文。
数据传输阶段的变化
完成了,三次握手后,客户端就可以发送第一个 ** **TCP 数据报文了,假设客户端即将要发送 10 字节的数据,流程图下:
在这里插入图片描述
客户端发送 10 字节的数据,通常 TCP 数据报文的控制位是 [PSH, ACK],此时该 TCP 数据报文的序列号和确认号分别设置为:
- 序列号设置为 client_isn + 1。客户端上一次发送报文是 ACK 报文(第三次握手),该报文的 seq = client_isn + 1,由于是一个单纯的 ACK 报文,没有携带用户数据,所以 len = 0。根据公式 1(_序列号 = 上一次发送的序列号 + len_),可以得出当前的序列号为 client_isn + 1 + 0,即 client_isn + 1。
- 确认号设置为 server_isn + 1。没错,还是和第三次握手的 ACK 报文的确认号一样,这是因为客户端三次握手之后,发送 TCP 数据报文 之前,如果没有收到服务端的 TCP 数据报文,确认号还是延用上一次的,其实根据公式 2 你也能得到这个结论。
可以看到,客户端与服务端完成 TCP 三次握手后,发送的第一个 「TCP 数据报文的序列号和确认号」都是和「第三次握手的 ACK 报文中序列号和确认号」一样的。
接着,当服务端收到客户端 10 字节的 TCP 数据报文后,就需要回复一个 ACK 报文,此时该报文的序列号和确认号分别设置为:
- 序列号设置为 server_isn + 1。服务端上一次发送报文是 SYN-ACK 报文,序列号为 server_isn,根据公式 1(_序列号 = 上一次发送的序列号 + len。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为 + 1_),所以当前的序列号为 server_isn + 1。
- 确认号设置为 client_isn + 11 。服务端上一次收到的报文是客户端发来的 10 字节 TCP 数据报文,该报文的 seq = client_isn + 1,len = 10。根据公式 2(_确认号 = 上一次收到的报文中的序列号 + len_),也就是将「收到的 TCP 数据报文中的序列号 client_isn + 1,再加上 10(len = 10) 」的值作为了确认号,表示自己收到了该 10 字节的数据报文。
之前有读者问,如果客户端发送的第三次握手 ACK 报文丢失了,处于 SYN_RCVD 状态服务端收到了客户端第一个 TCP 数据报文会发生什么?
刚才前面我也说了,发送的第一个 「TCP 数据报文的序列号和确认号」都是和「第三次握手的 ACK 报文中序列号和确认号」一样的,并且该 TCP 数据报文也有将 ACK 标记位置为 1。如下图:
在这里插入图片描述
所以,服务端收到这个数据报文,是可以正常完成连接的建立,然后就可以正常接收这个数据包了。
四次挥手阶段的变化
最后,我们来看看四次挥手阶段中,序列号和确认号的变化。
数据传输阶段结束后,客户端发起了 FIN 报文,请求服务端端开该 TCP 连接,此时就进入了 TCP 四次挥手阶段,如下图。
在这里插入图片描述
客户端发送的第一次挥手的序列号和确认号分别设置为:
- 序列号设置为 client_isn + 11。客户端上一次发送的报文是 [PSH, ACK] ,该报文的 seq = client_isn + 1, len = 10,根据公式 1(_序列号 = 上一次发送的序列号 + len_),可以得出当前的序列号为 client_isn + 11。
- 确认号设置为 server_isn + 1。客户端上一次收到的报文是服务端发来的 ACK 报文,该报文的 seq = server_isn + 1,是单纯的 ACK 报文,不携带用户数据,所以 len 为 0。那么根据公式 2(确认号 = 上一次收到的序列号 + len),可以得出当前的确认号为 server_isn + 1 + 0 (len = 0),也就是 server_isn + 1。
服务端发送的第二次挥手的序列号和确认号分别设置为:
- 序列号设置为 server_isn + 1。服务端上一次发送的报文是 ACK 报文,该报文的 seq = server_isn + 1,而该报文是单纯的 ACK 报文,不携带用户数据,所以 len 为 0,根据公式 1(_序列号 = 上一次发送的序列号 + len_),可以得出当前的序列号为 server_isn + 1 + 0 (len = 0),也就是 server_isn + 1。
- 确认号设置为 client_isn + 12。服务端上一次收到的报文是客户端发来的 FIN 报文,该报文的 seq = client_isn + 11,根据公式 2(_确认号= _上一次_收到的序列号 + len,特殊情况,如果收到报文是 SYN 报文或者 FIN 报文,则改为 + 1_),可以得出当前的确认号为 client_isn + 11 + 1,也就是 client_isn + 12。
服务端发送的第三次挥手的序列号和确认号还是和第二次挥手中的序列号和确认号一样。
- 序列号设置为 server_isn + 1。
- 确认号设置为 client_isn + 12。
客户端发送的四次挥手的序列号和确认号分别设置为:
- 序列号设置为 client_isn + 12。客户端上一次发送的报文是 FIN 报文,该报文的 seq = client_isn + 11,根据公式 1(_序列号 = 上一次发送的序列号 + len。特殊情况,如果收到报文是 SYN 报文或者 FIN 报文,则改为 + 1_),可以得出当前的序列号为 client_isn + 11 + 1,也就是 client_isn + 12。
- 确认号设置为 server_isn + 2。客户端上一次收到的报文是服务端发来的 FIN 报文,该报文的 seq = server_isn + 1,根据公式 2(_确认号 = _上一次_收到的序列号 + len,特殊情况,如果收到报文是 SYN 报文或者 FIN 报文,则改为 + 1_),可以得出当前的确认号为 server_isn + 1 + 1,也就是 server_isn + 2。
实际抓包图
在这里贴一个,实际过程中的抓包图。
在这里插入图片描述
套入我的万能公式,发送的 TCP 报文:
- 公式一:序列号 = 上一次发送的序列号 + len(数据长度)。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为 上一次发送的序列号 + 1。
- 公式二:确认号 = 上一次收到的报文中的序列号 + len(数据长度)。特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为上一次收到的报文中的序列号 + 1。
懂了这套公式之后,相信你在看这类的抓包图中序列号和确认号的变化的时候,就不会没有逻辑了。
怎么样,学废了吗,溜啦溜啦!
更多网络文章
网络基础篇
HTTP 篇
- HTTP 常见面试题
- HTTP/1.1如何优化?
- HTTPS RSA 握手解析
- HTTPS ECDHE 握手解析
- HTTPS 如何优化?
- HTTP/2 牛逼在哪?
- HTTP/3 强势来袭
- 既然有 HTTP 协议,为什么还要有 RPC?
TCP 篇
- TCP 三次握手与四次挥手面试题
- TCP 重传、滑动窗口、流量控制、拥塞控制
- TCP 实战抓包分析
- TCP 半连接队列和全连接队列
- 如何优化 TCP?
- 如何理解是 TCP 面向字节流协议?
- 为什么 TCP 每次建立连接时,初始化序列号都要不一样呢?
- SYN 报文什么时候情况下会被丢弃?
- 四次挥手中收到乱序的 FIN 包会如何处理?
- 在 TIME_WAIT 状态的 TCP 连接,收到 SYN 后会发生什么?
- TCP 连接,一端断电和进程崩溃有什么区别?
- 拔掉网线后, 原本的 TCP 连接还存在吗?
- tcp_tw_reuse 为什么默认是关闭的?
- HTTPS 中 TLS 和 TCP 能同时握手吗?
- TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗?
- TCP 有什么缺陷?
- 如何基于 UDP 协议实现可靠传输?
- TCP 和 UDP 可以使用同一个端口吗?
- 服务端没有 listen,客户端发起连接建立,会发生什么?
- 没有 accpet,可以建立 TCP 连接吗?
- 用了 TCP 协议,数据一定不会丢吗?
IP 篇
TCP 序列号和确认号是如何变化的?的更多相关文章
- TCP序列号和确认号
TCP序列号和确认号详解 在网络分析中,读懂TCP序列号和确认号在的变化趋势,可以帮助我们学习TCP协议以及排查通讯故障,如通过查看序列号和确认号可以确定数据传输是否乱序.但我在查阅了当前很多资料后发 ...
- 转 TCP中的序号和确认号
在网络分析中,读懂TCP序列号和确认号在的变化趋势,可以帮助我们学习TCP协议以及排查通讯故障,如通过查看序列号和确认号可以确定数据传输是否乱 序.但我在查阅了当前很多资料后发现,它们大多只简单介绍了 ...
- 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)
原文见:http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/ from:ht ...
- TCP:WireShark分析,序列号Seq和确认号Ack
转载自 http://blog.csdn.net/a19881029/article/details/38091243 序列号为当前端成功发送的数据位数,确认号为当前端成功接收的数据位数,SYN标志位 ...
- [转] 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)
点击阅读原译文 原文见:http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/ ...
- TCP头部分析与确认号的理解
1.TCP的特点: 基于字节流面向连接可靠传输缓冲传输全双工流量控制 2.头部格式和说明 图源百度.如下图示,就是TCP包的头部结构.可以看到这个头部最少有4x5=20个字节. 另外还需要理解TCP协 ...
- 韩顺刚-tcp报文头协议详细分析第一包数据:序号是0,发送数据的长度是0,因为没有收到对端的数据,所以确认号是0, Syn的标志位设置成1,这里没有发送的数据,只发送TCP的20个字节的头部
TCP报文段首部格式 大部分TCP报文头部都是20个字节,有的数据包要加上选项. 上面一行代表4个字节,源端口和目的端口都是2个字节. TCP协议是面向字节流的协议 TCP是一段一段分块的发送数据的 ...
- TCP报文送达确认ACK
TCP数据包中的序列号(Sequence Number)不是以报文段来进行编号的,而是将连接生存周期内传输的所有数据当作一个字节流,序列号就是整个字节流中每个字节的编号.一个TCP数据包中包含多个字节 ...
- TCP报文到达确认(ACK)机制
TCP数据包中的序列号(Sequence Number)不是以报文段来进行编号的,而是将连接生存周期内传输的所有数据当作一个字节流,序列号就是整个字节流中每个字节的编号.一个TCP数据包中包含多个字节 ...
随机推荐
- day20--Java集合03
Java集合03 8.LinkedList 1)linkedList底层实现了双向链表和双端队列的特点 2)可以添加任意元素(元素可以重复),包括null 3)线程不安全,没有实现同步 LinkedL ...
- Java学习--流程控制
Java学习 流程控制 用户交互Scanner Scanner对象 Java通过Scanner类获取用户的输入 基本语法: Scanner scanner = new Scanner(System.i ...
- 面试突击75:SpringBoot 有几种读取配置文件的方法?
Spring Boot 中读取配置文件有以下 5 种方法: 使用 @Value 读取配置文件. 使用 @ConfigurationProperties 读取配置文件. 使用 Environment 读 ...
- wbr 文本换行规则标签
<wbr/>标签规定在文本中的何处适合添加换行符.如果文本太长,浏览器可能会在错误的位置换行,那么可以使用<wbr/>标签来添加单词换行时机. Talk is cheap. S ...
- Codeforces 1715E - Long Way Home
又是废掉的一个div2啊 第一次在学校熬夜打cf,开心还看到了自己最喜欢的斜率优化ohhh 链接 :E - Long Way Home 看到那个平方就可以靠感觉认为是斜率优化了.... 感觉似不似有点 ...
- PHP实现服务器文件预览
PHP实现服务器里面的所有文件进行预览跟手机文件夹一样 服务器创建一个index.php文件 点我查看 <?php // errors ini_set('display_errors', 1); ...
- Clickhouse基准测试实践
1.概述 本篇博客将对MySQL.InfluxDB.Clickhouse在写入时间.聚合查询时间.磁盘使用等方面的性能指标来进行比较. 2.内容 比较的数据集,是使用的Clickhouse官网提供的6 ...
- window桌面背景图片
通过修改注册表项: \HKEY_CURRENT_USER\Control Panel\Desktop下的几个值,及可以将我们想要的图片设置成桌面的背景图 TileWallpaper Wallpap ...
- SQL order by 语句对null值排序
记order by 语句对null值排序: 目录 记order by 语句对null值排序: MySQL: Oracle: SqlServer: MySQL: 将null值放在最后 select * ...
- VS 正则匹配快速查询
**************************************************************************************************** ...