跳至主要內容

理解TCP序列号Seq和确认号Ack

Mr.Dylin...大约 7 分钟HTTPA_HTTP1.HTTP

原文地址 www.bianchengquan.comopen in new window

如果你正在读这篇文章,很可能你对 TCP“非著名”的 “三次握手” 或者说 “SYN,SYN/ACK,ACK” 已经很熟悉了。不幸的是,对很多人来说,对 TCP 的学习就仅限于此了。 尽管年代久远,TCP 仍是

如果你正在读这篇文章,很可能你对 TCP“非著名”的 “三次握手” 或者说 “SYN,SYN/ACK,ACK” 已经很熟悉了。不幸的是,对很多人来说,对 TCP 的学习就仅限于此了。

尽管年代久远,TCP 仍是一个相当复杂并且值得研究的协议。这篇文章的目的是让你能够更加熟练的检查 Wireshark 中的 TCP 序列号和确认号。

TCP Header 方便回忆

翻译作者: sean-zou

译文地址: https://blog.csdn.net/a19881029/article/details/38091243

英文原文: https://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/

在我们开始之前,确保在 Wireshark 中打开示例(请到作者原文中下载)并亲自实践一下

方便大家下载,提供百度网盘地址链接: https://pan.baidu.com/s/17me3DSka1kv6M8BhqTYmZQ 提取码: 6gyj

示例中仅包含一个单独的 HTTP 请求,请求的流程是:web 浏览器向 web 服务器请求一个单独的图片文件,服务器返回一个成功的响应(HTTP/1.1200 OK),响应中包含请求的文件。右键示例文件中任意一个 TCP 包并且选择 Follow TCP Stream 就可在单独的窗口查看原始的 TCP 流

img

客户端请求使用红色显示,服务端响应使用蓝色显示

img

TCP 三次握手(参见:http://blog.csdn.net/a19881029/article/details/30241561)

TCP 在其协议头中使用大量的标志位或者说 1 位(bit)布尔域来控制连接状态,我们最感兴趣的 3 个标志位如下:

SYN - 创建一个连接

FIN - 终结一个连接

ACK - 确认接收到的数据

就像我们看见的那样,一个包中有可以设置多个标志位

选择 Wireshark 中的 “包”1 并且展开中间面板的 TCP 层解析,然后展开 TCP 头中的标志位域,这里我们可以看见所有解析出来的 TCP 标志位,需要注意的是,“包 1” 设置了 SYN 标志位

img

使用同样的方式操作 “包 2”。可以看到 "包 2" 设置了 2 个标志位:ACK - 用来确认收到客户端的 SYN 包,SYN - 用来表明服务端也希望建立 TCP 连接

img

从客户端发来的 “包 3” 只设置了 ACK 标志位。这 3 个包完成了最初的 TCP3 次握手

img

序列号和确认号

TCP 会话的每一端都包含一个 32 位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收

当某个主机开启一个 TCP 会话时,他的初始序列号是随机的,可能是 0 和 4,294,967,295 之间的任意值,然而,像 Wireshark 这种工具,通常显示的都是相对序列号 / 确认号,而不是实际序列号 / 确认号,相对序列号 / 确认号是和 TCP 会话的初始序列号相关联的。这是很方便的,因为比起真实序列号 / 确认号,跟踪更小的相对序列号 / 确认号会相对容易一些

比如,在 “包 1” 中,最初的相对序列号的值是 0,但是最下方面板中的 ASCII 码显示真实序列号的值是 0xf61c6cbe,转化为 10 进制为 4129057982

如果想要关闭相对序列号 / 确认号,可以选择 Wireshark 菜单栏中的 Edit -> Preferences ->protocols ->TCP,去掉 Relative sequence number 后面勾选框中的√即可

img

需要注意的是,文章接下来的部分依然使用相对序列号 / 确认号

为了更好的理解在整个 TCP 会话期间,TCP 序列号和确认号是如何工作的,我们可以使用 Wireshark 内置的绘制流功能,选择菜单栏中的 Statistics ->Flow Graph...->TCP flow ->OK

img

Wireshark 会自动创建一个 TCP 流的图形摘要

img

每行代表一个单独的 TCP 包,左边列显示时间,中间列显示包的方向、TCP 端口、段长度和设置的标志位,右边列以 10 进制的方式显示相关序列号 / 确认号,在这里选中任意行会高亮主窗口中该行所关联的包

我们可以利用这个流图更好的理解序列号和确认号是如何工作的

包 1

TCP 会话的每一端的序列号都从 0 开始,同样的,确认号也从 0 开始,因为此时通话还未开始,没有通话的另一端需要确认(我使用的 Wireshark 版本和原作者不同,Wireshark1.10.2 中,包 1 不显示确认号)

包 2

服务端响应客户端的请求,响应中附带序列号 0(由于这是服务端在该次 TCP 会话中发送的第一个包,所以序列号为 0)和相对确认号 1(表明服务端收到了客户端发送的包 1 中的 SYN)

需要注意的是,尽管客户端没有发送任何有效数据,确认号还是被加 1,这是因为接收的包中包含 SYN 或 FIN 标志位(并不会对有效数据的计数产生影响,因为含有 SYN 或 FIN 标志位的包并不携带有效数据)

包 3

和包 2 中一样,客户端使用确认号 1 响应服务端的序列号 0,同时响应中也包含了客户端自己的序列号(由于服务端发送的包中确认收到了客户端发送的 SYN,故客户端的序列号由 0 变为 1)

此时,通信的两端的序列号都为 1,通信两端的序列号增 1 发生在所有 TCP 会话的建立过程中

包 4

这是流中第一个携带有效数据的包(确切的说,是客户端发送的 HTTP 请求),序列号依然为 1,因为到上个包为止,还没有发送任何数据,确认号也保持 1 不变,因为客户端没有从服务端接收到任何数据

需要注意的是,包中有效数据的长度为 725 字节

包 5

当上层处理 HTTP 请求时,服务端发送该包来确认客户端在包 4 中发来的数据,需要注意的是,确认号的值增加了 725(725 是包 4 中有效数据长度),变为 726,简单来说,服务端以此来告知客户端端,目前为止,我总共收到了 726 字节的数据,服务端的序列号保持为 1 不变

包 6

这个包标志着服务端返回 HTTP 响应的开始,序列号依然为 1,因为服务端在该包之前返回的包中都不带有有效数据,该包带有 1448 字节的有效数据

包 7

由于上个数据包的发送,TCP 客户端的序列号增长至 726,从服务端接收了 1448 字节的数据,客户端的确认号由 1 增长至 1449

在抓包文件的主体部分,我们可以看到上述过程的不断的重复,客户端的序列号一直是 726,因为客户端除了最初的 725 字节数据没有再向服务端发送数据,服务端的序列号则与此相反,由于服务端不断的发送 HTTP 响应,故其序列号一直在增长

序列号为当前端成功发送的数据位数,确认号为当前端成功接收的数据位数,SYN 标志位和 FIN 标志位也要占 1 位

关闭连接

img

包 38

在确认了服务端发送过来的最后一个数据段之后,客户端将处理整个 HTTP 响应并决定不需要进一步通信了。此时客户端发送设置了 FIN 标志位的包 38,其确认号和之前的包 37 一样

包 39

服务端通过将确认号加 1 的方式回应客户端期望关闭连接的请求(这里和包 2 中确认 SYN 标志位时所作的操作是一样的),同时设置当前包的 FIN 标志位

包 40

客户端发送最终序列号 727,通过将确认号加 1 的方式确认服务端的 FIN 包

此时,通信双方都终结了会话并且可以释放用于维持会话所占用的资源

上次编辑于:
贡献者: zddbic