当前位置: 首页 > news >正文

TCP | 序列号和确认号 [逐包分析] | seq / ack 详解

注 : 本文为 “TCP 序号(seq)与确认序号(ack)” 相关文章合辑

英文引文,机翻未校。

中文引文,略作重排。

如有内容异常,请看原文。


Understanding TCP Seq & Ack Numbers [Packet-by-Packet]

理解 TCP 序列号和确认号 [逐包分析]

January 2, 2024
Wireshark
Author: Celal Dogan
Reviewer: Deepak Prasad

Getting started with TCP Sequence and Acknowledgement Numbers

开始了解 TCP 序列号和确认号

TCP (Transmission Control Protocol) is a connection oriented and highly reliable protocol. Before data exchange between two parties, it requires to establish a connection, using TCP 3-way handshaking. The connection remains active until it gets terminated. During 3-way handshaking both sides synchronize (SYN) and acknowledge (ACK) each other. In another saying, they inform each other about what kind of settings they would like to use during the connection establishment. The settings include Sequence Number, Maximum Segment Size, if SACK is permitted or not, Window Scale, Window Size etc. See below for a SYN packet which contains an initiator (a client) settings.
TCP(传输控制协议)是一种面向连接且高度可靠的协议。在两方进行数据交换之前,需要通过 TCP 三次握手建立连接。连接一直保持活动状态,直到被终止。在三次握手中,双方会进行同步(SYN)和确认(ACK)。换句话说,它们会告知对方在建立连接时希望使用的设置类型。这些设置包括序列号、最大报文段长度、是否允许 SACK、窗口缩放、窗口大小等。下面是一个包含发起方(客户端)设置的 SYN 数据包。

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
可靠性是 TCP 的一个强大特性。TCP 确保一端发送的所有数据包都能被另一端接收,跟踪哪些数据包已成功接收,重新发送丢失的数据包,并指定在另一端重新组装数据的顺序。简而言之,TCP 主要通过序列号和确认号来提供这种可靠性。TCP 序列号和确认号是用于跟踪连接期间发送和接收的每个字节的计数器。
In this article, we will closely examine Sequence Number and Acknowledgement Number with Wireshark. For better understanding, we will capture a TCP flow and analyse it. I will visit the first web page published on the internet, which is pretty simple. If you wonder to see what它 is like, link is here: The World Wide Web project
在本文中,我们将通过 Wireshark 仔细研究序列号和确认号。为了更好地理解,我们将捕获一个 TCP 流并进行分析。我将访问互联网上发布的第一个网页,它非常简单。如果你想看看它是什么样子,链接在这里:万维网项目

Capturing a TCP Flow

捕获 TCP 流

  1. Open Wireshark and create a capture filter like below.
    打开 Wireshark 并创建如下捕获过滤器。

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]

  1. Navigate to The World Wide Web project from your browser.
    从浏览器访问 万维网项目。

  2. Stop capturing, now we should have the packets. See my captures below.
    停止捕获,现在我们应该有了数据包。看看我的捕获结果。

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]

  1. Go to StatisticsFlow Graph to see more details.
    前往 统计流图 以查看更多详细信息。

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
The figure above explains everything about the flow. First 3 packets --SYN, SYN/ACK and ACK-- are used to establish a connection before any data is exchanged. This step is called TCP 3-way handshaking. Next, the client sends a http GET request on the top of TCP and the server responds it back with a http 200 OK, which indicates that the request has succeeded. The last 4 packets are exchanged to tear down the connection.
上图解释了关于流的所有内容。前三个数据包——SYN、SYN/ACK 和 ACK——用于在任何数据交换之前建立连接。这一步称为 TCP 三次握手。接下来,客户端在 TCP 上发送一个 http GET 请求,服务器以 http 200 OK 响应,表示请求成功。最后四个数据包用于拆除连接。

Breaking down to Packet by Packet Analysis

逐包分析

Packet Number 1

第 1 个数据包

We will analyse the flow packet by packet, starting from the first packet. For visualization, see screen shot below.
我们将逐包分析流量,从第一个数据包开始。为了可视化,请参阅下面的屏幕截图。

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
This is the first packet (it is also called SYN packet) from the client to the server with source and destination port of 62834, 80 respectively.
这是客户端到服务器的第一个数据包(也称为 SYN 数据包),源端口和目的端口分别为 62834 和 80。

All data in a TCP connection are numbered, starting at a randomly chosen ISN (Initial Sequence Number). Although the first packet (SYN) does not contain any data, it consumes one sequence number and as a result the actual data begins at ISN+1. For easy understanding, Wireshark starts ISN from zero which is called “relative sequence number” while in the screen shot above, we can clearly see the client has set its real sequence number to 332215980. Relative sequence number is just for easy analyzing. Since this is the first packet in the flow, acknowledgement number is set to zero. With these settings, the client informs the server that it will use some options and asks the server to send its options as well in the next packet (SYN/ACK).
TCP 连接中的所有数据都从一个 随机选择 的 ISN(初始序列号)开始编号。尽管第一个数据包(SYN)不包含任何数据,但它会消耗一个序列号,因此实际数据从 ISN+1 开始。为了便于理解,Wireshark 从零开始 ISN,这被称为“相对序列号”。在上面的屏幕截图中,我们可以清楚地看到客户端将其真实序列号设置为 332215980。相对序列号只是为了便于分析。由于这是流中的第一个数据包,确认号设置为零。通过这些设置,客户端告知服务器它将使用一些选项,并要求服务器在下一个数据包(SYN/ACK)中发送其选项。

Packet Number 2

第 2 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
The second packet (it is also called SYN/ACK packet) from the server to the client is pretty similar to the first packet, except ACK flag being set to 1 this time. Even though, this packet is not carrying any data but connection settings, the acknowledgement number is increased by 1 which tells the client it has received the SYN packet while it sets its sequence number to zero.
从服务器到客户端的第二个数据包(也称为 SYN/ACK 数据包)与第一个数据包非常相似,只是这次 ACK 标志被设置为 1。尽管这个数据包没有携带任何数据,只有连接设置,但确认号增加了 1,这告诉客户端它已经收到了 SYN 数据包,同时将其序列号设置为零。

Packet Number 3

第 3 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
This is the last packet (it is also called ACK packet) for TCP 3-way handshaking. The client increases its sequence and acknowledgement number by 1, letting the server know it has received its SYN/ACK packet. From this point, the sequence and the ack numbers will increase only after one end has sent or received some data.
这是 TCP 三次握手的最后一个数据包(也称为 ACK 数据包)。客户端将其序列号和确认号各增加 1,告知服务器它已收到其 SYN/ACK 数据包。从这一点开始,序列号和确认号只有在一端发送或接收了一些数据后才会增加。

Packet Number 4

第 4 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
This is the first packet that contains some actual data (373 bytes of HTTP Get request), which is also called TCP payload. The sequence numbers are accumulated during the conversation. In another saying, the client let the server know how much data it has sent in total by sequence number. it also specifies amount of data in total it has received from the server by acknowledgement number. Since, it has not sent or received any data before, the sequence and the acknowledgement numbers remain 1.
这是第一个包含实际数据(373 字节的 HTTP GET 请求)的数据包,也称为 TCP 负载。序列号在对话过程中不断累积。换句话说,客户端通过序列号让服务器知道它总共发送了多少数据,同时也通过确认号指定了它从服务器总共接收了多少数据。由于它之前没有发送或接收过任何数据,因此序列号和确认号保持为 1。

Packet Number 5

第 5 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
This packet does not carry any data as you see from len: 0. Once the server receives 373 bytes of data, it needs to let the client know that it has received the data with ACK flag set. Since the server has not sent any data before, it sets its sequence number to 1 while the acknowledgement number increases by 373 to 374. In short, the server tells the client it got 373 bytes of data and it expects new data starting from number of 374.
len: 0 可以看出,这个数据包没有携带任何数据。一旦服务器收到 373 字节的数据,它需要通过设置 ACK 标志来告知客户端它已经收到了数据。由于服务器之前没有发送过任何数据,它将其序列号设置为 1,而确认号增加了 373,达到 374。简而言之,服务器告诉客户端它收到了 373 字节的数据,并期望从 374 开始接收新数据。

Packet Number 6

第 6 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
After receiving the http GET request, the server creates a http response and breaks it into 2 pieces, since total response size exceeds TCP maximum segment size (1452 bytes, it is present in the second packet) which both side agreed on during TCP 3-way handshaking. The length of the data is 1452 bytes. The server sends the packet with the same sequence and acknowledgement number.
在收到 HTTP GET 请求后,服务器创建了一个 HTTP 响应,并将其分成两部分,因为总响应大小超过了 TCP 最大报文段大小(1452 字节,这在第二个数据包中有体现),这是双方在 TCP 三次握手期间同意的。数据的长度为 1452 字节。服务器以相同的序列号和确认号发送了该数据包。

Packet Number 7

第 7 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
This is the second piece of the http response from the server with size of 998 bytes. you can see the sequence number has increased by 1452, because it sent that amount of data to the client in the previous packet while the acknowledgement number still remains the same.
这是服务器发来的 HTTP 响应的第二部分,大小为 998 字节。你可以看到序列号增加了 1452,因为它在前一个数据包中向客户端发送了这么多数据,而确认号仍然保持不变。

Packet Number 8

第 8 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
The server has no data to send and it wants to acknowledge the client that it would like to terminate the TCP connection with the fin flag set. The packet carries no data. The sequence number increases by 998 to 2451, which indicate that it has sent 2450 bytes until now. Since there has not been any change in amount of data receiving from the client, the acknowledgement number remains the same.
服务器没有数据要发送,它希望通过设置 FIN 标志来告知客户端它希望终止 TCP 连接。该数据包不携带任何数据。序列号增加了 998,达到 2451,这表明它到目前为止已经发送了 2450 字节。由于从客户端接收的数据量没有变化,确认号保持不变。

Packet Number 9

第 9 个数据包
Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
The client acknowledges the server that it has received its data by setting the acknowledgment number to 2452, which indicates that the server should send the data starting from 2452 next time (if it has any).
客户端通过将确认号设置为 2452 来告知服务器它已经收到了数据,这表明服务器下次(如果有)应从 2452 开始发送数据。

Packet Number 10

第 10 个数据包

Understanding TCP Seq & Ack Numbers [Packet-by-Packet]
With this packet, the client informs the server that it also would like to terminate the connection with the fin flag set.
通过这个数据包,客户端告知服务器它也希望通过设置 FIN 标志来终止连接。


获取 TCP 序列和确认编号基础知识的简单示例

TCP Sequence and Acknowledgement Numbers Explained

TCP 序列和确认编号说明

TCP Sequence (seq) and Acknowledgement (ack) numbers help enable ordered reliable data transfer for TCP streams. The seq number is sent by the TCP client, indicating how much data has been sent for the session (also known as the byte-order number). The ack number is sent by the TCP server, indicating that is has received cumulated data and is ready for the next segment.
TCP 序列 (seq) 和确认 (ack) 编号有助于为 TCP 流实现有序可靠的数据传输。 seq 编号由 TCP 客户端发送,指示已为会话发送的数据量(也称为字节顺序号)。ack 编号由 TCP 服务器发送,表示已收到累积数据,并已准备好进行下一段。

The TCP seq and ack numbers are coordinated with one another and are key values during the TCP handshake, TCP close, and, of course, while data is transferred between the client and server.
TCP seq 和 ack 编号相互协调,是 TCP 握手、TCP 关闭期间的关键值,当然,在客户端和服务器之间传输数据时也是如此。

[By default, Wireshark converts all sequence and acknowledgement numbers into relative numbers. This means that all SEQ and ACK numbers always start at 0 for the first packet seen in each conversation.]
[默认情况下,Wireshark 将所有序列和确认编号转换为相对编号。这意味着对于每个会话中看到的第一个数据包,所有 SEQ 和 ACK 编号始终从 0 开始。

The picture below shows a real example of TCP sequence and acknowledgment numbers in a TCP flow diagram. The key variable is the TCP segment length for each TCP segment sent in the session.
下图显示了 TCP 流程图中 TCP 序列和确认编号的真实示例。key 变量是会话中发送的每个 TCP 分段的 TCP 分段长度。

img

The client sends the first segment with seq=1 and the length of the segment is 669 bytes. The server responds with an ack=670 which tells the client that the next expected segment will have a sequence number is 670.
客户端发送 seq=1 的第一个分段,该分段的长度为 669 字节。 服务器以 ack=670 响应,这告诉客户端下一个预期的 segment 将具有 670 的序列号。

The next segment the client sends has seq=670 and the len is now 1460 bytes. In turn, the server responds with ack=2130 (670 + 1460). This cycle continues until the end of the TCP session.
客户端发送的下一个分段具有 seq=670,len 现在是 1460 字节。 反过来,服务器以 ack=2130 (670 + 1460) 响应。 此循环将持续到 TCP 会话结束。

Again, note that the length value is from the TCP segment length, not the Layer 2 frame length nor the IP packet length.
同样,请注意,length 值来自 TCP 分段长度,而不是第 2 层帧长度或 IP 数据包长度。

Seq and Ack in Wireshark

Wireshark 中的 Seq 和 Ack

Client sends seq=1 and tcp segment length=669

客户端发送 seq=1 且 tcp 段长度 = 669

img

Server responds with ack=670

服务器响应 ack=670

img

Client sends segment with seq=670 and length=1460

客户端发送 seq=670 且 length=1460 的 segment

img

Server responds with ack=2130

服务器响应 ack=2130

img

This is just a simple example to get the basics of TCP sequence and acknowledgement numbers.


Understanding TCP Sequence and Acknowledgment Numbers

了解 TCP 序列和确认编号

By stretch | Monday, June 7, 2010 at 2:15 a.m. UTC

If you’re reading this, odds are that you’re already familiar with TCP’s infamous “three-way handshake,” or “SYN, SYN/ACK, ACK.” Unfortunately, that’s where TCP education ends for many networkers. Despite its age, TCP is a relatively complex protocol and well worth knowing intimately. This article aims to help you become more comfortable examining TCP sequence and acknowledgment numbers in the Wireshark packet analyzer.
如果您正在阅读本文,那么您很可能已经熟悉 TCP 臭名昭著的“三次握手”或“SYN、SYN/ACK、ACK”。不幸的是,对于许多网络工作者来说,TCP 教育到此为止。尽管 TCP 年代久远,但它是一个相对复杂的协议,非常值得深入研究。本文旨在帮助您更舒适地在 Wireshark 数据包分析器中检查 TCP 序列和确认号。

Before we start, be sure to open the example capture in Wireshark and play along.
在开始之前,请务必在 Wireshark 中打开示例捕获并一起玩。

The example capture contains a single HTTP request to a web server, in which the client web browser requests a single image image file, and the server returns an HTTP/1.1 200 (OK) response which includes the file requested. You can right-click on any of the TCP packets within this capture and select Follow TCP Stream to open the raw contents of the TCP stream in a separate window for inspection. Traffic from the client is shown in red, and traffic from the server in blue.
示例捕获包含对 Web 服务器的单个 HTTP 请求,其中客户端 Web 浏览器请求单个图像图像文件,服务器返回 HTTP/1.1 200 (OK) 响应,其中包括请求的文件。您可以右键单击此捕获中的任何 TCP 数据包,然后选择 Follow TCP Stream (跟随 TCP 流) 以在单独的窗口中打开 TCP 流的原始内容进行检查。来自客户端的流量显示为红色,来自服务器的流量显示为蓝色。

在这里插入图片描述

The Three-Way Handshake

三次握手

TCP utilizes a number of flags, or 1-bit boolean fields, in its header to control the state of a connection. The three we’re most interested in here are:
TCP 在其标头中使用许多标志或 1 位布尔字段来控制连接的状态。我们在这里最感兴趣的三个是:

  • SYN - (Synchronize) Initiates a connection
    SYN - (同步)启动连接
  • FIN - (Final) Cleanly terminates a connection
    FIN - (最终)完全终止连接
  • ACK - Acknowledges received data
    ACK - 确认接收到的数据

As we’ll see, a packet can have multiple flags set.
正如我们将看到的,一个数据包可以设置多个标志。

Select packet #1 in Wireshark and expand the TCP layer analysis in the middle pane, and further expand the “Flags” field within the TCP header. Here we can see all of the TCP flags broken down. Note that the SYN flag is on (set to 1).
在 Wireshark 中选择数据包 #1 并展开中间窗格中的 TCP 层分析,然后进一步展开 TCP 标头中的“Flags”字段。在这里,我们可以看到所有细分的 TCP 标志。请注意,SYN 标志处于打开状态(设置为 1)。

在这里插入图片描述

Now do the same for packet #2. Notice that it has two flags set: ACK to acknowledge the receipt of the client’s SYN packet, and SYN to indicate that the server also wishes to establish a TCP connection.
现在对数据包 #2 执行相同的作。请注意,它设置了两个标志:ACK 确认收到客户端的 SYN 数据包,SYN 表示服务器也希望建立 TCP 连接。

在这里插入图片描述

Packet #3, from the client, has only the ACK flag set. These three packets complete the initial TCP three-way handshake.
来自客户端的数据包 #3 仅设置了 ACK 标志。这三个数据包完成初始 TCP 三次握手。

Sequence and Acknowledgment Numbers

序列号和确认编号

The client on either side of a TCP session maintains a 32-bit sequence number it uses to keep track of how much data it has sent. This sequence number is included on each transmitted packet, and acknowledged by the opposite host as an acknowledgment number to inform the sending host that the transmitted data was received successfully.
TCP 会话两端的客户端都维护一个 32 位序列号,用于跟踪它发送的数据量。此序列号包含在每个传输的数据包中,并由对面主机确认为确认号,以通知发送主机已成功接收传输的数据。

When a host initiates a TCP session, its initial sequence number is effectively random; it may be any value between 0 and 4,294,967,295, inclusive. However, protocol analyzers like Wireshark will typically display relative sequence and acknowledgment number in place of the field’s actual value. These values are relative to the initial sequence number of that stream. This is handy, as it is much easier to keep track of relatively small, predictable numbers rather than the actual numbers sent on the wire.
当主机启动 TCP 会话时,其初始序列号实际上是随机的;它可以是介于 0 和 4,294,967,295 之间的任何值(包括 0 和 4,294,967,295)。但是,像 Wireshark 这样的协议分析器通常会显示相对序列和确认号,而不是字段的实际值。这些值是相对于该流的初始序列号的。这很方便,因为跟踪相对较小的、可预测的数字比跟踪电报上发送的实际数字要容易得多。

For example, the initial relative sequence number shown in packet #1 is 0 (naturally), while the ASCII decode in the third pane shows that the actual sequence number is 0xf61c6cbe, or 4129057982 decimal.
例如,数据包 #1 中显示的初始相对序列号为 0(自然),而第三个窗格中的 ASCII 解码显示实际序列号为 0xf61c6cbe 或4129057982十进制。

在这里插入图片描述

The display of relative sequence numbers can optionally be disabled by navigating to Edit > Preferences… and un-checking Relative sequence numbers and window scaling under TCP protocol preferences. However, be aware that the remainder of this article will reference relative sequence and acknowledgment numbers only.
可以选择通过导航到 Edit > Preferences…并取消选中 TCP 协议首选项下的 Relative sequence numbers and window scaling。但是,请注意,本文的其余部分将仅引用相对序列和确认编号。

To better understand how sequence and acknowledgment numbers are used throughout the duration of a TCP session, we can utilize Wireshark’s built-in flow graphing ability. Navigate to Statistics > Flow Graph…, select TCP flow and click OK. Wireshark automatically builds a graphical summary of the TCP flow.
为了更好地了解序列和确认编号在整个 TCP 会话期间的使用方式,我们可以利用 Wireshark 的内置流图功能。导航到 统计信息 > 流图…,选择 TCP 流,然后单击 确定。Wireshark 会自动构建 TCP 流的图形摘要。

在这里插入图片描述

Each row represents a single TCP packet. The left column indicates the direction of the packet, TCP ports, segment length, and the flag(s) set. The column at right lists the relative sequence and acknowledgment numbers in decimal. Selecting a row in this column also highlights the corresponding packet in the main window.
每行表示一个 TCP 数据包。左列指示数据包的方向、TCP 端口、分段长度和设置的标志。右侧的列以十进制列出相对序列和确认编号。在此列中选择一行还会在主窗口中突出显示相应的数据包。

We can use this flow graph to better understand how sequence and acknowledgment numbers work.
我们可以使用此流程图来更好地了解序列和确认数字的工作原理。

Packet #1

数据包 #1

Each side of a TCP session starts out with a (relative) sequence number of zero. Likewise, the acknowledgment number is also zero, as there is not yet a complementary side of the conversation to acknowledge.
TCP 会话的每一端都以 0 的(相对)序列号开头。同样,确认数也为零,因为对话中还没有需要确认的补充面。

(Note: The version of Wireshark used for this demonstration, 1.2.7, shows the acknowledgment number as an apparently random number. This believed to be a software bug; the initial acknowledgment number of a session should always be zero, as you can see from inspecting the hex dump of the packet.)
(注意:用于此演示的 Wireshark 版本 1.2.7 将确认编号显示为明显的随机数。这被认为是一个软件错误;会话的初始确认号应始终为零,从检查数据包的十六进制转储中可以看出。

Packet #2

数据包 #2

The server responds to the client with a sequence number of zero, as this is its first packet in this TCP session, and a relative acknowledgment number of 1. The acknowledgment number is set to 1 to indicate the receipt of the client’s SYN flag in packet #1.
服务器使用序列号 0 响应客户端,因为这是它在此 TCP 会话中的第一个数据包,相对确认编号为 1。确认编号设置为 1,以指示在数据包 #1 中收到客户端的 SYN 标志。

Notice that the acknowledgment number has been increased by 1 although no payload data has yet been sent by the client. This is because the presence of the SYN or FIN flag in a received packet triggers an increase of 1 in the sequence. (This does not interfere with the accounting of payload data, because packets with the SYN or FIN flag set do not carry a payload.)
请注意,尽管客户端尚未发送任何负载数据,但确认编号已增加 1。这是因为收到的数据包中存在 SYN 或 FIN 标志会触发序列中增加 1。(这不会干扰有效负载数据的核算,因为设置了 SYN 或 FIN 标志的数据包不会携带有效负载。

Packet #3

数据包 #3

Like in packet #2, the client responds to the server’s sequence number of zero with an acknowledgment number of 1. The client includes its own sequence number of 1 (incremented from zero because of the SYN).
与数据包 #2 中一样,客户端使用确认号 1 响应服务器的序列号 0。客户端包括自己的序列号 1(由于 SYN 的原因,从零开始递增)。

At this point, the sequence number for both hosts is 1. This initial increment of 1 on both hosts’ sequence numbers occurs during the establishment of all TCP sessions.
此时,两台主机的序列号均为 1。两个主机的序列号上的初始增量 1 发生在建立所有 TCP 会话期间。

Packet #4

数据包 #4

This is the first packet in the stream which carries an actual payload (specifically, the client’s HTTP request). The sequence number is left at 1, since no data has been transmitted since the last packet in this stream. The acknowledgment number is also left at 1, since no data has been received from the server, either.
这是流中携带实际有效负载(具体而言,客户端的 HTTP 请求)的第一个数据包。序列号保留为 1,因为自此流中的最后一个数据包以来未传输任何数据。确认号也保留为 1,因为也没有从服务器收到任何数据。

Note that this packet’s payload is 725 bytes in length.
请注意,此数据包的有效负载长度为 725 字节。

Packet #5

数据包 #5

This packet is sent by the server solely to acknowledge the data sent by the client in packet #4 while upper layers process the HTTP request. Notice that the acknowledgment number has increased by 725 (the length of the payload in packet #4) to 726; e.g., “I have received 726 bytes so far.” The server’s sequence number remains at 1.
此数据包由服务器发送,仅用于确认客户端在数据包 #4 中发送的数据,而上层则处理 HTTP 请求。请注意,确认编号增加了 725(数据包 #4 中有效负载的长度)到 726;例如,“到目前为止,我收到了 726 个字节。” 服务器的序列号保持为 1。

Packet #6

数据包 #6

This packet marks the beginning of the server’s HTTP response. Its sequence number is still 1, since none of its packets prior to this one have carried a payload. This packet carries a payload of 1448 bytes.
此数据包标记服务器的 HTTP 响应的开始。它的序列号仍然是 1,因为在此之前的数据包都没有携带有效负载。此数据包携带 1448 字节的有效负载。

Packet #7

数据包 #7

The sequence number of the client has been increased to 726 because of the last packet it sent. Having received 1448 bytes of data from the server, the client increases its acknowledgment number from 1 to 1449.
由于客户端发送的最后一个数据包,客户端的序列号已增加到 726。从服务器收到 1448 字节的数据后,客户端将其确认号从 1 增加到 1449。

For the majority of the capture, we will see this cycle repeat. The client’s sequence number will remain steady at 726, because it has no data to transmit beyond the initial 725 byte response. The server’s sequence number, in contrast, continues to grow as it sends more segments of the HTTP response.
对于大部分捕获,我们将看到此循环重复。客户端的序列号将稳定在 726,因为除了初始 725 字节响应之外,它没有要传输的数据。相反,服务器的序列号会随着发送 HTTP 响应的更多段而继续增长。

Tear-down

拆解

Packet #38

数据包 #38

After acknowledging the last segment of data from the server, the client processes the HTTP response as a whole and decides no further communication is needed. Packet #38 is sent by the client with the FIN flag set. Its acknowledgment number remains the same as in the prior packet (#37).
在确认来自服务器的最后一段数据后,客户端将 HTTP 响应作为一个整体进行处理,并决定不需要进一步的通信。数据包 #38 由设置了 FIN 标志的客户端发送。其确认编号与上一个数据包 (#37) 中的编号相同。

Packet #39

数据包 #39

The server acknowledges the client’s desire to terminate the connection by increasing the acknowledgment number by one (similar to what was done in packet #2 to acknowledge the SYN flag) and setting the FIN flag as well.
服务器通过将确认编号增加 1(类似于数据包 #2 中确认 SYN 标志的操作)并设置 FIN 标志来确认客户端终止连接的愿望。

Packet #40

数据包 #40

The client sends its final sequence number of 727, and acknowledges the server’s FIN packet by incrementing the acknowledgment number by 1 to 22952.
客户端发送其最终序列号 727,并通过将确认编号增加 1 到 22952 来确认服务器的 FIN 数据包。

At this point, both hosts have terminated the session and can release the software resources dedicated to its maintenance.
此时,两台主机都已终止会话,可以释放专用于其维护的软件资源。


理解 TCP 序列号(Sequence Number)和确认号(Acknowledgment Number)

sean-zou 于 2014-07-25 13:00:55 发布

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

如果你正在读这篇文章,很可能你对 TCP “非著名” 的 “三次握手” 或者说 “SYN,SYN/ACK,ACK” 已经很熟悉了。不幸的是,对很多人来说,对 TCP 的学习就仅限于此了。尽管年代久远,TCP 仍是一个相当复杂并且值得研究的协议。这篇文章的目的是让你能够更加熟练的检查 Wireshark 中的 TCP 序列号和确认号

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

示例中仅包含一个单独的 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 包

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


TCP 序号(seq)与确认序号(ack)详解

huaishu 已于 2022-04-26 10:37:16 修改

TCP

关于 TCP 序号(seq)和确认序号(ack)

序号(seq) 用来标识从 TCP 发端向 TCP 收端发送的数据字节流,它表示在这个报文段中的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用序号对每个字节进行计数。序号是 32 位的无符号数,序号到达 2 32 − 1 2^{32} - 1 2321 后又从 0 开始。

当建立一个新的连接时,SYN 标志位为 1。序号字段包含由这个主机选择的该连接的初始序号 ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为这个 ISN 加 1,因为 SYN 标志位消耗了一个序号。既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号(ack) 应当是上次已成功收到数据字节序号加 1。只有 ACK 标志位(下面介绍)为 1 时确认序号字段才有效。

发送 ACK 无需任何代价,因为 32 位的确认序号字段和 ACK 标志位一样,总是 TCP 首部的一部分。因此,我们看到一旦一个连接建立起来,这个字段总是被设置,ACK 标志位也总是被设置为 1。

TCP 为应用层提供全双工服务。这意味着数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。

详细解释

  • sequence number(序号)

    • 表示的是我方(发送方)这边,这个 packet 的数据部分的第一位应该在整个 data stream 中所在的位置。(注意这里使用的是“应该”。因为对于没有数据的传输,如 ACK,虽然它有一个 seq,但是这次传输在整个 data stream 中是不占位置的。所以下一个实际有数据的传输,会依旧从上一次发送 ACK 的数据包的 seq 开始)。

    • SYN 和 FIN 的影响:SYN 和 FIN 虽然没有携带数据,但会让下一次传输的 packet 的 seq 增加 1。这是因为它们被视为特殊的“控制字节”,占用了序号空间。

    • ACK 的影响:ACK 的传输不会让下一次的传输 packet 的 seq 加 1,因为 ACK 本身不携带数据,不会占用序号空间。

  • acknowledge number(确认序号)

    • 表示的是期望的对方(接收方)的下一次 sequence number 是多少。
    • 它是基于发送方收到的最后一个正确数据字节的序号加 1。例如,如果发送方收到的最后一个数据包的序号是 X X X,并且该包包含 N N N 个字节的数据,那么确认序号(ack)应该是 X + N X + N X+N

总结

  • 序号(seq)

    • 用于标识发送方数据流中的每个字节。
    • SYN 和 FIN 会让 seq 增加 1,而 ACK 不会。
    • 序号是 32 位无符号数,循环使用。
  • 确认序号(ack)

    • 表示接收方期望收到的下一个字节的序号。
    • 只有当 ACK 标志位为 1 时,确认序号才有效。
    • 确认序号是基于已成功接收的数据字节序号加 1。

这些机制确保了 TCP 的可靠性和顺序性,使得数据能够在复杂的网络环境中可靠传输。

三次握手过程

TCP 连接的建立是通过三次握手来实现的。

序号方向seqackSYNACK
1A->B10000 (ISN)010
2A<-B20000 (ISN)10000+1=1000111
3A->B1000120000+1=2000101

解释:

  1. (A) –> [SYN] –> (B)
    A 向 B 发起连接请求,以一个随机数初始化 A 的 seq,这里假设为 10000,此时 ACK = 0。
  2. (A) <– [SYN/ACK] <–(B)
    B 收到 A 的连接请求后,也以一个随机数初始化 B 的 seq,这里假设为 20000,意思是:你的请求我已收到,我这方的数据流就从这个数开始。B 的 ACK 是 A 的 seq 加 1,即 10000 + 1 = 10001。
  3. (A) –> [ACK] –> (B)
    A 收到 B 的回复后,它的 seq 是它的上个请求的 seq 加 1,即 10000 + 1 = 10001,意思也是:你的回复我收到了,我这方的数据流就从这个数开始。A 此时的 ACK 是 B 的 seq 加 1,即 20000 + 1 = 20001。

数据传输过程

序号方向seqack数据长度数据包长度
23A->B400007000014601514
24A<-B7000040000+1514-54=41460054
25A->B4146070000+54-54=7000014601514
26A<-B7000041460+1514-54=42920054

解释:

  1. B 接收到 A 发来的 seq = 40000, ack = 70000, size = 1514 的数据包。
  2. 于是 B 向 A 也发一个数据包,告诉 A,你的上个包我收到了。A 的 seq 就以它收到的数据包的 ack 填充,ack 是它收到的数据包的 seq 加上数据包的大小(不包括:以太网协议头 = 14 字节,IP 头 = 20 字节,TCP 头 = 20 字节),以证实 B 发过来的数据全收到了。
  3. A 在收到 B 发过来的 ack 为 41460 的数据包时,一看到 41460,正好是它的上个数据包的 seq 加上包的大小,就明白,上次发送的数据包已安全到达。于是它再发一个数据包给 B。
  4. B -> A 这个正在发送的数据包的 seq 也以它收到的数据包的 ack 填充,ack 就以它收到的数据包的 seq(70000)加上包的 size(54)填充,即 ack = 70000 + 54 - 54(全是头长,没数据项)。通过 tcpdump 发现确认包 ack,确认传输过程中最后字节长度。

减去 54 的原因:以太网封装格式(链路层使用的是 Ethernet II 格式,这个格式有 14 字节以太网首部 + 4 字节以太网尾部):

  • 应用数据 = size - 14 - 20 - 20 = size - 54。(假设 IP 首部和 TCP 首部都没有可选选项)
  • 为什么不减去以太网尾部的 4 字节呢?
    因为在物理层上网卡要先去掉前导同步码和帧开始定界符,然后对帧进行 CRC 检验,如果帧校验和错,就丢弃此帧。如果校验和正确,就判断帧的目的硬件地址是否符合自己的接收条件(目的地址是自己的物理硬件地址、广播地址、可接收的多播硬件地址等),如果符合,就将帧交“设备驱动程序”做进一步处理。这时我们的抓包软件才能抓到数据,因此,抓包软件抓到的是去掉前导同步码、帧开始分界符、FCS 之外的数据。

四次挥手过程

序号方向seqackFINACK
1A->B800009000011
2A<-B9000080000+1=8000101
3A<-B900008000111
4A->B8000190000+1=9000101

解释:

  1. (A) –> [FIN/ACK] –> (B)
    客户端 A 没有要发送给服务端 B 的数据了,想要关闭连接,则发送一个 FIN = 1,ACK = 1 的包,告诉 B 可以关闭连接了,我没有什么数据要给你了。
  2. (A) <– [ACK] <– (B)
    然后 B 会发送 ACK = 1 的包给 A,告诉 A 我知道你没有什么想给我的了,但是我还有数据要给你,你先等下,我先不想 FINISH 呢。
  3. (A) <– [FIN/ACK] <– (B)
    等 B 把数据都发送给 A 之后,B 会再次发送一个包,这次 FIN = 1,表示我这边也想关闭了,咱俩一起关吧。在 2 和 3 之间,可能还会有很多 B -> A 的传递,ack 均为 80001。
  4. (A) –> [ACK] –> (B)
    然后 A 回应一个 ACK,表示我知道了,一起关吧。B 收到这个 ACK 后,就会 CLOSE。但是实际上 A 不会直接 CLOSE,还会进入一个等待时间状态 TIME_WAIT,持续 2 倍的 MSL(Maximum Segment Lifetime,报文段在网络上能存活的最大时间)。过了这个状态,才会 CLOSE。

为什么要等待一段时间?原因有二:

  1. 保证 TCP 的全双工连接能够可靠关闭
    假如 A 发送的最后一次 ACK 丢包了,没有被 B 收到,那 B 超时之后,会再次发送一个 FIN 包,然后这个包被处于 TIME_WAIT 状态的 A 收到,A 会再次发送一个 ACK 包,并重新开始计时,一直循环这个过程,直到 A 在 TIME_WAIT 的整个过程中都没有收到 B 发过来的 FIN 包,这说明 B 已经收到了 A 的 ACK 包并 CLOSE 了,因此 A 这时候才可以安心 CLOSE。如果 A 没有 TIME_WAIT 状态而是直接 close,那么当 ACK 丢包之后,B 会再次发送一个 FIN 包,但是这个包不会被 A 回应,因此 B 最终会收到 RST,误以为是连接错误,不符合可靠连接的要求。因此需要等待 ACK 报文到达 B。RST 是 TCP 数据报中 6 个控制位之一,6 个控制位的作用如下:

    控制位名称描述
    URG 紧急当 URG = 1 时,它告诉系统此报文中有紧急数据,应优先传送(比如紧急关闭),这要与紧急指针字段配合使用。
    ACK 确认仅当 ACK = 1 时确认号字段才有效。建立 TCP 连接后,所有报文段都必须把 ACK 字段置为 1。
    PSH 推送若 TCP 连接的一端希望另一端立即响应,PSH 字段便可以“催促”对方,不再等到缓存区填满才发送。
    RST 复位若 TCP 连接出现严重差错,RST 置为 1,断开 TCP 连接,再重新建立连接。
    SYN 同步用于建立和释放连接。
    FIN 终止用于释放连接,当 FIN = 1,表明发送方已经发送完毕,要求释放 TCP 连接。
  2. 保证这次连接的重复数据段从网络中消失
    如果 A 直接 close 了,然后向 B 发起了一个新的 TCP 连接,可能两个连接的端口号相同。一般不会有什么问题,但是如果旧的连接有一些数据堵塞了,没有到达 B,新的握手连接就已经到 B 了,那么这时候,由于区分不同 TCP 连接是依据套接字,因此 B 会将这批迟到的数据认为是新的连接的数据,导致数据混乱(源 IP 地址和目的 IP 地址以及源端口号和目的端口号的组合称为 套接字,新旧连接的套接字很有可能相同)。如果我们终止一个客户程序,并立即重新启动这个客户程序,则这个新客户程序将不能重用相同的本地端口。服务端处于被动关闭,不会出现该状态。

总结

通常 TCP 在接收到数据时并不立即发送 ACK;相反,它推迟发送,以便将 ACK 与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带 ACK)。

挥手关闭过程中,处于半关闭状态,被动关闭状态传输的数据 ack 都是一致的。

类型握手 (SYN) 或终止 (FIN)传输数据包为 0
seq (自己发送)上次发送 seq + 1上次发送 seq + 数据长度上次发送 seq
ack (接收对方)上次接收 seq + 1上次接收 seq + 数据长度上次接收 seq

实战列表

实战列表
 
4、
seq:上一次发送时为,中 seq 为 0 且为 SYN 数据包,所以这一次的 seq 为 1(0 增加 1)。
ack:上次接收到时为,中 seq 为 0,且为 SYN 数据包,所以可预计,server 端下一次 seq 为 1(0 增加 1)。
5、
seq:上一次发送时为,中 seq 为 0,且为 SYN 数据包,所以这一次的 seq 为 1(0 增加 1)。
ack:上一次接收时为,中的 seq 为 1,数据包的长度为 725,所以可以预计,下一次 client 端的 seq 为 726(1 + 725)。
6、
seq:上一次发送时为,中 seq 为 1,但为 ACK 数据包,所以数据长度为 0 且不会驱使 seq 加 1,所以这一次的 seq 为 1(1 + 0)。
ack:上一次接收时为,中的 seq 为 1,数据包的长度为 725,所以可以预计,下一次 client 端的 seq 为 726(1 + 725)。


一文搞懂 TCP 连接中的序号 seq 和确认应答号 ack!

Joy T 于 2024-03-16 21:21:18 发布

简述

在 TCP(传输控制协议)中,序号(Sequence Number,简称 seq)和确认应答号(Acknowledgment Number,简称 ack)是协议头部非常关键的字段,它们共同确保了 TCP 的可靠性和数据按顺序传输的特性。

序号(Sequence Number)

  • 含义:序号是指一个 TCP 报文段中第一个字节的数据序列标识。它表示在一个 TCP 连接中,该报文段所携带的数据的开始位置。序号是用来保证数据传输的顺序性和完整性的。
  • 作用:在 TCP 连接建立时,双方各自随机选择一个初始序列号(ISN)。随后传输的每个报文段的序号将基于这个初始值递增,其增量为该报文段所携带的数据量(字节数)通过这种方式,接收方可以根据序号重组乱序到达的数据片段,确保数据的正确顺序和完整性。如果接收到的报文段不连续,接收方可以通过 TCP 的重传机制请求发送方重新发送缺失的数据。

例如,如果一个报文段被赋予了序号 100,并且它包含 100 字节的数据,那么这个报文段就代表了从序号 100 到 199 的数据。随后的报文段将继续这个序列。继续上面的例子,下一个报文段可能会开始于序号 200,如果它包含 50 字节的数据,那么它就代表了从序号 200 到 249 的数据。

确认应答号(Acknowledgment Number)

-含义:确认应答号是接收方期望从发送方接收到的下一个报文段的序号。它实质上是接收方告诉发送方:“我已经成功接收到了哪个序号之前的所有数据,请从这个序号开始发送后续的数据。

  • 作用:确认应答号用于实现可靠性传输。当一个报文段被接收方正确接收时,接收方会发送一个 ACK 报文,其中包含的确认应答号是接收到的数据加上 1(即接收方期望接收的下一个数据的序号)。通过检查这个确认应答号,发送方能够知道其发送的数据是否已被接收方正确接收,并据此决定是否需要重传某些数据段。

为什么要使用序号和确认应答号

序号和确认应答号机制使得 TCP 能够:

  • 确保数据的顺序性:即使数据片段在网络中的传输过程中顺序被打乱,接收方也能根据序号正确地重组这些数据。
  • 检测丢包:如果发送方发送的数据长时间未被确认(即没有收到对应的 ACK 报文),它会判断这些数据可能已丢失,并将其重新发送。
  • 实现流量控制和拥塞控制:通过调整发送未被确认数据的量(即控制窗口大小),TCP 可以根据网络条件动态调整数据发送的速率,避免网络拥塞。

规定 SYN/FIN 报文段如果不挟带数据就要消耗一个序号,这个是为了什么呢?

在 TCP 协议中,建立连接(通过三次握手过程)和释放连接(通过四次挥手过程)的特殊报文段(SYN 和 FIN 报文)规定了它们不能携带数据负载,但是要消耗一个序号。这样设计主要有以下几个目的:

1. 确保可靠性

序号的消耗提供了一种机制,确保连接建立和终止的过程可靠且按顺序执行。通过将 SYN 和 FIN 报文视为占用一个序号空间,TCP 能够将这些控制报文纳入到整个序号系统中,保持序号的连续性和一致性。

2. 简化处理

通过要求 SYN 和 FIN 报文消耗一个序号,TCP 使得这些控制报文能够被统一地处理,就像其他携带数据的报文段一样。这简化了 TCP 的实现,因为所有类型的报文段都遵循相同的序号和确认机制

3. 同步序号空间

-连接建立时,SYN 报文的序号(ISN)为连接的双方同步序号空间提供了一个起点。消耗一个序号意味着接下来的数据传输将从这个初始序列号加一开始,这有助于避免序号的重复使用,并清晰地界定每个连接的序号空间。

-连接释放时,FIN 报文的序号确保了连接关闭前所有的数据都已被确认接收。消耗一个序号使得连接的正常关闭(即确认所有传输数据的接收)和新的数据传输(在新的连接中)清晰地分隔开来。

4. 防止旧连接的数据干扰

在网络中延迟的旧连接数据包(如果有的话)在新连接建立时不会被误认为是新数据,因为SYN 报文在每个新连接中初始化一个新的序列号空间(这时旧连接中的数据包的序号是根据旧连接的 SYN/ACK 传承的,所以在新的连接建立时,整个连接的序列号空间就变了。所有的 seq 和 ack 都会与过去的旧连接不同)。这个机制有助于区分并隔离属于旧连接的数据包和新连接的数据包。


TCP 序列号和确认号是如何变化的?

原创 小林 coding 小林 coding 2022 年 10 月 26 日 14:32 广东

很多人对 TCP 序列号和确认号的变化都是懵懵懂懂的,只知道三次握手和四次挥手过程中,ACK 报文中确认号要 +1,然后数据传输中 TCP 序列号和确认号的变化就不知道了。

这次就跟大家聊聊以下过程中,TCP 序列号和确认号是如何变化的?

  • 三次握手中 TCP 序列号和确认号的变化

  • 数据传输中 TCP 序列号和确认号的变化

  • 四次挥手中 TCP 序列号和确认号的变化

万能公式

我根据经验总结了一条万能公式。发送的 TCP 报文

  • 公式一:序列号 = 上一次发送的序列号 + len(数据长度)。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为上一次发送的序列号 + 1。

  • 公式二:确认号 = 上一次收到的报文中的序列号 + len(数据长度)。特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为上一次收到的报文中的序列号 + 1。

可能有点抽象,接下来举一些实际的场景,加深对这个万能公式的理解。

先给大家看看 TCP 序列号和确认号在 TCP 头部的哪个位置。可以看到,这两个字段都是 32 位。

图片
TCP 头部

这里重点关注这三个字段的作用:

  • 序列号:在建立连接时由内核生成的随机数作为其初始值,通过 SYN 报文传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。

  • 确认号:指下一次「期望」收到的数据的序列号,发送端收到接收方发来的 ACK 确认报文以后,就可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。

  • 控制位:用来标识 TCP 报文是什么类型的报文,比如是 SYN 报文、数据报文、ACK 报文,FIN 报文等。

三次握手阶段的变化

先来说说三次握手中 TCP 序列号和确认号的变化。

假设客户端的初始化序列号为 client_isn,服务端的初始化序列号为 server_isn,TCP 三次握手的流程如下:

图片

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 字节的数据,流程图下:

图片

TCP 数据传输阶段

客户端发送 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 四次挥手阶段,如下图。

图片 TCP 四次挥手阶段

客户端发送的第一次挥手的序列号和确认号分别设置为:

  • 序列号设置为 client_isn + 11。客户端上一次发送的报文是 [PSH, ACK] ,该报文的 seq = client_isn + 1, len = 10,根据公式 1序列号 = 上一次发送的序列号 + len),可以得出当前的序列号为 client_isn + 1 + 10(len = 10),即 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。

懂了这套公式之后,相信你在看这类的抓包图中序列号和确认号的变化的时候,就不会没有逻辑了。


『面试问答』:TCP 序列号和确认号是如何变化的?

原创 王小智 编程十万问 2023 年 12 月 04 日 11:24 湖北

面试官:请说一下,TCP 序列号和确认号是如何变化的?

TCP 是一种面向连接的可靠传输协议,序列号和确认号是保证 TCP 可靠传输的一种重要机制。在 TCP 协议中,每个数据包都有一个序列号 seq 和一个确认号 ack。

图片

序列号表示这个数据包中的第一个字节在整个数据流中的位置。对于发送方来说,序列号用来跟踪已发送的字节数。而接收方则通过序列号来确定自己是否接收到正确的数据。

图片

确认号表示接收方期望下一个收到的字节的位置。接收方在收到数据后,发送确认号给发送方,告知已成功接收到的数据。发送方在接收到确认号后,会根据确认号来确定哪些数据已经被成功接收,哪些数据需要进行重传。

图片

在 TCP 三次握手时:

首先,客户端向服务器发送 SYN,产生一个随机数 x 作为客户端的初始 seq,所以此时 seq=x,ack=0。

接着,服务器回复 SYN 和 ACK,产生一个随机数 y 作为服务器的初始 seq,ack 等于上一个收到报文的 seq+1,也就是客户端 SYN 报文的 seq+1(SYN 报文,TCP 当做传输 1 个字节),即 x+1。所以此时 seq=y,ack=x+1。

最后,客户端回复服务器 ACK,seq 等于上一个自己发送报文的 seq+1, 也就是 SYN 报文的 seq+1,即 x+1。而 ack 等于上一个收到报文的 seq+1,即服务器 SYN 和 ACK 报文的 seq+1(SYN 报文,TCP 当做传输 1 个字节),即 y+1。所以此时 seq=x+1,ack=y+1。

图片

同一个 tcp 连接中,在第一次握手客户端生成随机数 x 作为初始 seq,第二次握手服务器生成随机数 y 作为初始 seq 后,后续报文的 seq 和 ack 的变化都是有规律的,每个 TCP 报文的 seq 都等于上一次自己发送报文的 seq 加上 tcp 负载数据长度,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则认为 TCP 负载数据长度为 1。而每个 TCP 报文的 ack 都等于上一次自己收到报文的 seq 加上 tcp 负载数据长度。如果上一次收到的是 SYN 报文或者 FIN 报文,则认为 TCP 负载数据长度为 1。

图片

图片


正常的三次握手时序图应该是什么样的?

科来 2024 年 07 月 30 日 17:01 北京

按照 RFC 793,正常三次握手的工作过程如下:

1、SYN(同步):客户端发送一个 TCP 的 SYN(同步)标志的数据包给服务器,请求建立一个连接。这个数据包中的 Seq 序列号字段被填充为客户端 ISN(初始序列号,Initial Sequence Number),Ack 确认号字段为空,即全 0。

2、SYN/ACK(同步确认):服务器收到客户端的 SYN 请求后,如果同意建立连接,则会发送一个 SYN+ACK 标志的数据包作为应答。这个数据包中的 Seq 序列号字段被填充为服务器 ISN,Ack 确认号字段被填充为客户端 ISN+1。

3、ACK(确认):客户端收到服务器的 SYN/ACK 应答后,会发送一个 ACK(确认)标志的数据包,确认收到服务器的初始序列号。这个数据包中的 Seq 序列号字段被填充为客户端 ISN+1,Ack 确认号字段被填充为服务器 ISN+1

经过上述三次交互,客户端和服务器连接建立成功,双方进入 TCP ESTABLISHED 状态,可以开始传输数据。通过 TCP 交易时序图功能,只要看到双方三次握手交互完成,且三次握手包不存在重传、重复 ACK、丢包等情况,则可以判断双方 TCP 连接建立正常,如图 3 所示:

图片

图 3:正常的三次握手

连接建立失败,包括哪些异常情况?

除正常的三次握手外,有时 TCP 还存在一些连接建立无响应的情况,如图 4 所示:

图片

图 4:连接建立失败 ——SYN 包无响应

如果网络中出现了 SYN 包无响应情况,一般可能由如下原因导致:

  • SYN 包未到达服务器。例如安全设备阻断、网络环路等原因。

  • SYN 包顺利到达服务器,但服务器忽略了该 SYN 包。例如时间戳问题、TIME_WAIT 状态、端口复用失败等原因。

  • SYN 包顺利到达服务器,但服务器停止响应 SYN 包。例如 TCP 资源限制,遭受 SYN Flood DDoS 攻击等原因。

  • SYN 包的目标服务器离线、宕机或网络中断。

SYN 包无响应说明问题大概率出在网络层面,应先暂时停止应用层面的问题排查。具体原因应采用多点位对比分析法,分别在客户端、服务器、中间链路抓取数据包,进行进一步对比分析,判断具体结果。

如果遇到 SYN/ACK 包无响应或者 ACK 包无响应的情况,应当采用同样的多点位对比分析法进行对比分析,从而判断具体的连接建立失败原因。

当然,连接建立失败并不仅仅包含无响应问题,还有一种常见的情况,即 RST(Reset)包的发送。RST 包是 TCP 协议中用来进行 “连接重置” 的数据包,如图 5 和图 6 所示:

图片

图 5:连接在建立阶段被重置

图片

图 6:连接在其他阶段被重置

RST 包的出现说明意味着连接被终止。由于 RST 包的发送机制和其背后的深层原因还有许多值得深入探讨的细节。


TCP 连接中为何会有 RST 包?

RST(Reset)包是 TCP 协议中用来进行 “连接重置” 的数据包,本文将围绕 RST 包进行详细展开讨论。

TCP RST 包,即 TCP 头部中 RST 位设置为 1 的数据包。根据 RFC 793,TCP RST 包用于终止一个现有的连接或拒绝一个连接请求。当一个 TCP 端点希望立即终止一个连接时,它会发送一个 RST 包。

因此,当客户端或服务器的任意一方需要拒绝连接,或认为连接出现错误时,即可以发送 RST 包重置当前 TCP 连接。RST 包在 TCP 时序图中的体现如图 1 所示:

图片

图 1:时序图中的 TCP RST 包

连接建立阶段的 RST 包如何分析?

在连接建立阶段,服务器可发送 RST 包拒绝连接建立,如图 2 所示,在连接建立阶段,服务器发送 RST 包,表示服务器拒绝连接。

图片

图 2:服务器拒绝连接

如果服务器发送 RST 包拒绝连接,一般可能由如下原因导致:

  • 服务器端口未开放:服务器在该端口未运行网络服务,或服务器上客户所请求的服务失效,则服务器会通过 RST 拒绝新连接。

  • 服务器 TCP 连接数达到极限:如果服务器设置了 tcp_abort_on_overflow=1,那么服务器在队列满时会发送 RST 包拒绝连接。

  • Time_Wait 状态:如果客户端使用的当前 socket 在上一个连接刚刚结束,且服务器当前 socket 处于 time_wait 状态,则此时使用该 socket 的新连接请求会被服务器拒绝,返回 RST 包。

  • SYN 包格式错误:客户端发送的 SYN 包携带了其它未经允许的标记(例如 FIN、URG 或其他标记),则服务器会拒绝连接并直接返回 RST

  • 防火墙策略不允许:如果客户端 IP 被禁止连接,则会话中会出现 RST 包,此种情况在后文中具体讨论。

如果在会话连接建立阶段出现了服务器 RST 的情况,建议从服务器位置分析流量,或在系统层面排查连接建立失败的原因。

除服务器外,在连接建立阶段,客户端也有可能发送 RST 包,如图 3、图 4 所示,在连接建立已经经历了 SYN 包、SYN/ACK 包的交互后,甚至三次握手完成后,客户端突然发送 RST 包,表示客户端拒绝连接。

图片

图 3:客户端在收到 SYN/ACK 包后发送 RST

图片

图 4:客户端在三次握手成功后立即发送 RST

对于图 3、图 4 中出现的情况,可以直接判断为客户端发生异常或客户端正在发起端口扫描攻击。其中图 3 的流量为典型客户端发起 TCP SYN 端口扫描时序图,图 4 的流量为典型的客户端发起 TCP Connect 端口扫描时序图,如遇到此类时序图,请使用策略封禁该客户端 IP 地址,并检查客户端 IP 地址的其它流量,观察其有无其它攻击行为。

数据传输阶段的 RST 包如何分析?

在数据传输阶段,客户端和服务器均可能随时发出 RST,此时 RST 的原因一定是连接出现异常。对于此类故障,常见如下原因:

  • 重传次数超限:TCP 具有重传机制,当 TCP 尝试多次重传而无法收到对方的确认(或对方发来的确认无法被接收处理,例如序列号错误、校验和错误等种种字段错误)后,重传的一方将会认为连接出现错误,停止重传并发送 RST 包重置连接。如图 5 所示。

图片

图 5:多次重传后,服务器认为会话错误发送 RST

  • 连接长时间无数据交互:当客户端和服务器之间长时间(例如 120 秒)无数据交互时,其中一方可能认为会话超时,会向发送 RST 包重置连接。如图 6 所示。在客户端和服务器中间流量经过负载均衡或防火墙的场景,RST 包也可能由负载或防火墙发出。

图片

图 6:120 秒无交互后,服务器认为会话超时发送 RST

如果在会话交互阶段出现了 RST 的情况,建议通过分析时序图判断重置的原因是错误或超时。如果怀疑 RST 包是从中间设备发出,可以通过对比多个位置的同时段流量,确定 RST 包来源,从而进一步排查故障原因。

被防火墙阻断的会话时序图是什么样的?

当 ACL 或安全策略匹配后的动作为 Reject 时,或安全设备是旁路部署无法直接丢弃流量时,安全设备会采用发送 RST 包方式去处理策略命中的会话。被 RST 的会话会因此中断。被安全策略阻断的会话如图 7 所示

图片

图 7:连接被其他设备 RST 阻断

通过图 7 可以看出,服务器响应了 SYN/ACK 包,而立刻回复了一个 RST,这是由于发送 RST 包的设备为中间的安全设备,在进行旁路阻断时,只能通过发送 RST 进行阻断,而无法拦截服务器已发出的 SYN/ACK 包。因此,从客户端处能够看到服务器 “同时回复” 了 SYN/ACK 包和 RST 包。

如果要问,为什么能判断这个 RST 一定是旁路阻断包,那可以仔细观察这些 RST 包,本文图 1、图 2、图 4、图 5、图 6 中的 RST 包,均为携带 RST,ACK 这两个标志位的 “真 RST 包”,而图 3、图 7 中的 RST 包是仅携带 RST 标志位的 “假 RST 包”,另外,如果能够对比 RST 包和 SYN/ACK 包的 IP TTL,则可以发现这两个包的 IP TTL 可能不同(也有部分设备能对 TCP RST 包的 IP TTL 拟真),说明其来自于两个不同的网络位置,如图 8 所示:

图片

图 8:“假 RST” 与 “真 RST” 的 TTL 不同

图 9 则描述了一种更加容易理解的旁路阻断:客户端发送 ClientHello 包后,中间的旁路设备阻断了客户端,但未向服务器发送 RST,导致服务器认为会话未中断,还在继续发送后续 ServerHello 数据包:

图片

图 9:旁路阻断 RST

总之,如果会话过程中出现 RST 包,需要考虑该 RST 包是否由中间旁路阻断设备发出,可以通过该 RST 包的 RST 位、序列号、IP TTL 等方式,或是直接多点抓包对比分析,综合判断。

连接断开阶段的 RST 包如何分析?

在连接断开阶段,也可能出现 RST 包,这种情况一般是由于接收 FIN 包的一方或中间的负载、安全设备存在关于连接断开的优化机制。因为会话如果经过 FIN 包四次断开结束,先发 FIN 包的一方会经过 TCP TIME_WAIT 状态,经过 2MSL 时间才会进入到 close 状态,彻底关闭连接。在 2MSL 时间段内,此 socket 不可用。而被 RST 重置的会话,则不存在 TIME_WAIT 状态,因此,一些连接在出现 FIN 包后,负载 / 安全设备认为该连接已经可以被结束,于是发送 RST 包快速关闭会话。这种方法虽然快捷但不符合 TCP 协议标准。被 RST 加速断开的会话如图 10 和图 11 所示:

图片

图 10:客户端 FIN 后出现 RST 快速断开连接

图片

图 11:服务器 FIN 后出现 RST 快速断开连接

在 TCP 连接的生命周期中,RST 包扮演了一个关键角色,通过时序图观察 RST 包出现时机的分析,我们可以看到 RST 包在连接建立、数据传输、异常阻断、连接断开阶段的出现原因和影响。

了解 RST 包的发送原因,是维护网络稳定性和安全性的重要技能。通过细致的 RST 包分析,可以有效地分析网络故障、优化网络性能和提升网络的安全性。


TCP 的核心组件(上):说说 TCP 序列号的运动规律

科来 2024 年 10 月 11 日 17:05 北京

本文将围绕 TCP 序列号(Sequence Number)、载荷长度(Payload Length)和下一包序列号(Next Sequence Number)进行着重探讨。

TCP 的 “序列号” 是什么?

TCP 协议为了实现可靠传输,将它自己要发送的每一个字节都进行了 “排序编号”。例如,当发送方要发送一个字符 a 时,字符 a 会被赋予一个序列号(Sequence Number),例如 1234567890。在 TCP 协议中,序列号的取值范围为 0-4296947295,关于 TCP 的序列号,在时序图中可通过如下位置直接观察:

图片

图 1:时序图中的 “序列号”

TCP 连接启动阶段,序列号如何确定?

万物皆有源,TCP 连接建立的初始包,序列号应该从哪个数字开始?这要引入一个概念:ISN(启动序列号,Initial Sequence Number),ISN 即客户端和服务器在会话首包使用的序列号,客户端与服务器使用的 ISN 号码是不同的。根据 RFC 793 的描述,产生 ISN 数字的算法应该尽可能的随机,从而避免序列号的冲突和序列号被攻击者猜测。

因此,客户端的 ISN 即 SYN 包的序列号,服务器的 ISN 即 SYN/ACK 包的序列号,ISN 的位置如图 2 所示。

图片

图 2:时序图中的 “ISN”

TCP 的 “载荷长度” 和 “下一包序列号” 又是什么?

要理解 TCP 的序列号如何工作,需要引入另外两个概念:载荷长度和下一包序列号。

载荷长度(Payload Length)为一个 TCP 数据包的载荷数据长度,即数据包不含二层、三层、四层数据的长度。时序图的右侧标记了每一个 TCP 数据包的载荷长度。通过图 2 可以看出,4 号包的载荷长度为 138,6 号包的载荷长度为 68,其它包由于仅为基础 TCP 交互,并未传输应用层数据,因此不存在载荷长度,如图 3 所示:

图片

图 3:时序图中的 “载荷长度”

下一包序列号(Next Sequence Number)为该数据包之后下一个数据包的序列号,计算方式为【当前包的序列号 + 载荷长度 = 下一包序列号】。时序图中每一数据包的序列号右侧显示了经过计算的,该包的下一包序列号。如图 4 所示:

图片

图 4:时序图中的的 “下一包序列号”

需要注意的是,序列号的编号单位是基于字节,而不是基于数据包。

TCP 序列号为何有时 + 1,有时 + 长度?

刚才说到,【当前包的序列号 + 载荷长度 = 下一包序列号】,看到这里可能会有细心读者问,图中 1 号 SYN 包和 7 号 FIN 包并不存在载荷长度,为何下一包序列号被设置为序列号 + 1?

这是由于 SYN 包和 FIN 包为连接中较重要的包,需要被确认,因此数据包的 SYN 位和 FIN 位被 TCP 视为 1 字节的载荷数据。所以,在 TCP 三次握手中,客户端 SYN 和服务器 SYN/ACK 包的下一包序列号为【当前包的序列号(ISN)+ 载荷长度(1 字节 SYN 位)= 下一包序列号(ISN+1)】。如下图所示:

图片

图 5:三次握手中的 “下一包序列号”

大多数人理解 TCP 两个连续包之间的序列号是 + 1 方式递增,其实这是一种理解误区。如果一定要按照 + 1 增长方式来理解 TCP 序列号,可理解为:连接建立和断开阶段的下一包序列号为【序列号 + 1】,数据传输阶段的下一包序列号为【序列号 + 长度】

从时序图看 TCP 序列号的运动规律

理解了上述概念后,我们再来根据【序列号 + 载荷长度 = 下一包序列号】这一公式梳理一下 TCP 的是如何安排序列号的。以下图为例:

图片

图 6:TCP 序列号的运动规律

  • 1 号包为客户端发往服务器方向的首包,序列号为 1638701981,即客户端 ISN,载荷长度为一个 SYN 位,因此下一包序列号为 1638701981+1=1638701982。因此,1 号包的下一包序列号为 1638701982。

  • 3 号包同样为客户端发往服务器方向,序列号为 1638701982,该包不存在载荷数据,因此,3 号包的下一包序列号为 1638701982+0,仍为 1638701982。

  • 接下来,客户端发送了 4 号包,其序列号为 1638701982,该包载荷长度为 138 字节,因此,4 号包的下一包序列号为 1638701982+138=1638702120

  • 最后,客户端发送了 7 号包,其序列号为 1638702120,该包载荷为一个 FIN 位,因此下一包序列号为 1638702120+1=1638702121

  • 2 号包为服务器发往客户端方向的首包,序列号为 432609823,即服务器 ISN,载荷长度为一个 SYN 位,因此下一包序列号为 432609823+1=432609824。因此,2 号包的下一包序列号为 432609824

  • 5 号包同样为服务器发往客户端方向,序列号为 432609824,该包不存在载荷数据,因此,5 号包的下一包序列号为 432609824+0,仍为 432609824。

  • 接下来,服务器发送了 6 号包,其序列号仍为 432609824,该包载荷长度为 68 字节,因此,6 号包的下一包序列号应为 432609824+68=432609892。

以上即是 TCP 序列号的运动规律。

本文中深入探讨了 TCP 序列号的概念,从时序图的视角揭示了序列号的运动规律。除序列号外,确认号也是 TCP 连接中另一个关键概念,确认号与序列号一起构成了 TCP 可靠传输的基础。下一篇文章中,我们将探讨 TCP 如何通过确认号来确认接收到的数据包,以及确认机制是如何工作的。


TCP 的核心组件(下):确认号与确认机制

科来 2024 年 10 月 30 日 17:01 北京

本文将继续围绕 TCP 确认相关机制 ——TCP 确认号(Acknowledgment Number)、累积确认(Cumulative Acknowledgement)和延迟确认(Delayed Acknowledgement)探讨。

TCP 的 “确认号” 是什么?

TCP 协议为了实现可靠传输,将它自己要发送的每一个字节都进行了 “排序编号”,即 Seq 确认号(此部分内容已在上文中详细讨论)。

接收数据的一方,需要通过确认号(Acknowledgement Number)对已经成功接收到的数据进行确认。例如发送方发送了 Seq 为 1234567890 的数据包,有效载荷长度为 5,那么接收方就需要将序列号 + 载荷长度进行计算,得到数字 1234567895,并将此数值填入 Ack 号字段,返回给发送方,表示对 1234567890 至 1234567894 这一段数据(5 字节数据)的确认。

在 TCP 协议中,确认号的取值范围为 0-4296947295,和序列号取值范围一致。关于 TCP 的确认号,在时序图中可通过如下位置直接观察:

图片

图 1:时序图中的 “确认号”

从时序图看 TCP 确认号的运动规律

理解了上述概念后,我们再来根据【序列号 + 载荷长度 = 确认号】这个公式,观察通信双方的确认号的运动规律

为了方便计算和观察,我们只取序列号中的后 4 位,这不会影响分析的结果和准确性。以图 2 为例,1 号包序列号为 1638701981,记作 1981:

图片

图 2:时序图中的 “确认号”

从时序图观察 TCP 累积确认机制

  • 1 号包序列号尾号为 1981,实际有效载荷 SYN 位为的 1 字节,因此服务器在 2 号包使用 1981+1=1982 作为确认号进行确认

  • 4 号包序列号尾号为 1982,实际有效载荷为 138 字节,因此服务器在 5 号包使用 1985+138=2120 作为确认号进行确认

  • 2 号包序列号尾号为 9823,实际有效载荷 SYN 位为的 1 字节,因此客户端在 3 号包使用 9823+1=9824 作为确认号进行确认

  • 6 号包序列号尾号为 9824,实际有效载荷为 68 字节,因此客户端在 7 号包使用 9824+68=9892 作为确认号进行确认。

RFC 793 文档中的 3.3 Sequence Number 小节中这样写到:

The acknowledgment mechanism employed is cumulative so that an acknowledgment of sequence number X indicates that all octets up to but not including X have been received.

确认机制是累积的,因此针对序列号 X 的确认,表示已接收到 X 之前但不包括 X 的所有字节。

因此,TCP 协议实际使用累积确认机制。这样做的好处是能在发生丢包情况时,接收方会收到不连续的数据,此时仅对接收到的连续数据部分进行确认,便于发送方发现传输中出现的丢包现象。关于丢包的相关话题,在后续文章中会详细讨论到。

另外,由于累积确认机制的存在,接收方无需针对每一个 TCP 包回复 ACK 进行确认,可以通过一个 ACK 包合并确认之前的所有数据包,能够节约传输成本。

对于 TCP 的累积确认机制,在时序图中也能有明确的展示,如图 3 所示:

图片

图 3:累积确认机制

在图 3 中,三次握手后,服务器连续发送了多个载荷长度为 1460 的数据包,其中 13 号包的序列号为 3828312369,长度为 1460。随后,客户端通过 14 号包的进行了累积确认,确认号为 3828312369+1460=3828313829,这表示包括 13 号包在内和之前的 12、11、10……4 号包均被成功接收。在时序图中点击选中 14 号包或 13 号包,选中包和选中包相关的确认 / 被确认包,箭头均会被标记为黄色。其中粗体黄色为当前选中包,细体箭头为选中包相关的确认 / 被确认包。这项功能便于使用者快速找到与某一包相关的确认 / 被确认包。

从时序图观察 TCP 累积确认机制

和 TCP 确认有关的机制,还包括延迟确认机制。延迟确认机制实现了将 ACK 包 “延迟一会儿再发送”,实现 “再攒一点数据一起进行确认” 的机制。在 telnet 或 ssh 这样的协议交互场景下,延迟确认机制能够有效减少 TCP 确认包的数量,提高交互效率。另外,延迟确认机制设计的初衷是为了避免 SWS 糊涂窗口综合征额外消耗 CPU 性能。目前,在很多操作系统中设计了关于延迟确认的实现,一般场景下的延迟确认时间为 200ms。

在 RFC 1122 文档中,对延迟确认机制进行了这样的描述:

A host that is receiving a stream of TCP data segments can increase efficiency in both the Internet and the hosts by sending fewer than one ACK (acknowledgment) segment per data segment received; this is known as a “delayed ACK” [TCP:5].

接收 TCP 数据段流的主机可以通过发送少于每个接收数据段一个确认(ACK)段来提高互联网和主机的效率;这被称为 “延迟确认”[TCP:5]。

A TCP SHOULD implement a delayed ACK, but an ACK should not be excessively delayed; in particular, the delay MUST be less than 0.5 seconds, and in a stream of full-sized segments there SHOULD be an ACK for at least every second segment.

TCP 应该实现延迟确认,但确认不应该过度延迟;特别是,延迟必须少于 0.5 秒,并且在一系列全尺寸段中,至少应该每第二个段就有一个 ACK。

这里提到了两个关键信息:

  1. 延迟确认时间必须少于 0.5 秒

  2. 每收到两个全尺寸数据段,即停止延迟,直接发送 ACK 包

在时序图中观察延迟确认的情况,如图 4 所示:

图片

图 4:延迟确认机制

如图 4 所示,根据《从时序图看 TCP-01:连接建立与三次握手》文章中描述的网络时延计算方法,可以测得客户端和服务器之间的 RTT 为 0.25ms,这意味着确认数据所花费的时间应在 0.25ms 左右。

观察客户端发送 4 号包后,服务器间隔 208ms 后才响应 ACK,这便是 TCP 的延迟确认机制在生效工作。

一个典型的故障实例

图 5 展示了一个故障网络环境的抓包,这个故障与 TCP 序列号和确认号有关。读者可以尝试分析该故障在流量上体现的信息。故障现象如下:

  1. 使用 A 运营商访问网站,无法打开完整页面

  2. 使用 B 运营商访问网站,则不存在此问题

  3. 抓包显示服务器多次重传了 HTTP 响应,但客户端不接收,直至连接超时重置

图片

图 5:故障抓包,抓包点位于客户端

本文深入探讨了 TCP 的确认机制,包括确认号的计算、累积确认机制以及延迟确认机制的介绍,以及在 CSNAS 时序图中如何观察这些现象。通过了解这些机制和时序图的分析,能够提升流量分析工程师在工作中快速分析解决故障的能力。


Wireshark 抓包工具 ——TCP 数据包 seq、ack 等概念解读

桶丁于 2013 年 11 月 18 日 15 时 20 分 03 秒发布

  1. 在 Wireshark 的数据包详情窗口中,若内容由中括号 “[]” 包围,则表示该部分为注释内容,在实际的数据包中并不占用字节空间。

  2. 在二进制窗口中,例如显示为 “DD 3D” 的内容,其表示的是两个字节的数据,每个字节由 8 位构成。

  3. 在 TCP 数据包中,“seq”(序列号)用于标识该数据包的序号。需要注意的是,该序号并非按照 1 依次递增,而是根据 TCP 包内数据的字节长度进行累加。举例来说,若包内数据长度为 21 字节,且当前从 IP1 发送至 IP2 的数据包的 “seq” 值为 10,那么下一个由 IP1 发送至 IP2 的数据包的 “seq” 值则为 10 + 21 = 31。

  4. 在分析 TCP 数据包时,应当将一个会话作为一个完整的研究对象。具体而言,该会话中的通讯仅发生在两个固定的 IP 地址以及两个固定的端口之间。一旦端口发生变化,则意味着建立的是不同的网络连接,不同连接之间的 “seq” 值不存在关联关系。

  5. TCP 数据包可分为包头部分和内容部分。TCP 包头的标准长度为 32 字节,而整个数据包的包头长度通常为 66 字节(但并非固定不变)。若整个数据包的长度恰好为 66 字节,那么其内容长度即为 0 字节。

  6. 每个 TCP 数据包均包含 “win”(窗口大小)和 “ack”(确认号)字段。“win” 字段用于告知对方本机当前还能够接收数据的滑动窗口大小。例如,若 A 发送至 B 的数据包中 “win” 值为 0,则表示 A 向 B 传达当前自身的滑动窗口已无可用空间,无法继续接收数据,这通常暗示 A 端存在一定的环境压力,如网络带宽已被占满等情况。

  7. “ack”(确认号)可以理解为一种应答机制。A 发送给 B 的 “ack” 值,是用于告知 B,A 已经接收到 B 发送的数据包,并且已经处理到了 “ack” 所标识的序号位置,同时指示 B 下次发送数据包时,其 “seq” 值应设置为该 “ack” 号。

  8. 在网络状况良好、滑动窗口未出现拥塞的情况下,第一个数据包的 “ack” 号通常与第二个数据包的 “seq” 号相等。然而,当网络出现拥塞时,由于滑动窗口采用缓存处理队列机制,这两个值会出现不一致的情况。

  9. 若 A 向 B 连续发送多个数据包,在此过程中 “seq” 号保持不变,而 “ack” 号持续增大,这表明 A 一直在接收 B 发送的数据,并且持续向 B 发送应答信息。

  10. 若 A 向 B 连续发送多个数据包,“seq” 号不断增大,而 “ack” 号始终保持不变,则说明 A 正在持续向 B 发送数据,此时无需向 B 发送应答,而是处于等待 B 进行应答的状态。

  11. 在实际的网络通讯中,可以在接收多个数据包之后,一次性发送一个应答信息,而不必对每个数据包都单独进行一一对应的应答操作。

  12. 当发送一个数据包后,若在较长时间内未收到对方的应答信息,发送端会重新发送该数据包。在 Wireshark 抓包工具中,此类重发的数据包会被标记为 “[TCP Retransmission]”,在数据包详情窗口中展开该标记,可以查看具体是对哪个数据包进行了重传操作。

  13. “[TCP Dup ACK ?#?]” 表示应答包的重传情况。

  14. 若在抓包过程中出现 “[TCP Previous segment not captured]” 的错误提示,则意味着网络数据传输出现了乱序现象,即后一个数据包已经被接收,但前一个数据包尚未被接收到。在这种情况下,也会触发数据包的重传机制。


via:

  • Understanding TCP Seq & Ack Numbers [Packet-by-Packet] | GoLinuxCloud
    https://www.golinuxcloud.com/tcp-sequence-acknowledgement-numbers/

  • TCP Sequence and Acknowledgement Numbers Explained – MadPackets
    https://madpackets.com/2018/04/25/tcp-sequence-and-acknowledgement-numbers-explained/

  • Understanding TCP Sequence and Acknowledgment Numbers - Packet Life
    http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/

  • 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)_relative sequence number-CSDN博客
    https://blog.csdn.net/a19881029/article/details/38091243

  • TCP 包的 seq 和 ack 号计算方法_tcp seq-CSDN 博客
    https://blog.csdn.net/huaishu/article/details/93739446

  • TCP协议中的 seq/ack 序号是如何变化的? - 简书
    https://www.jianshu.com/p/15754b4e9458

  • [TCP 的 seq 和 ack 号计算方法 - CSDN 博客
    https://blog.csdn.net/happyrocking/article/details/78198776

  • 使用 tcpdump 分析 TCP 三次握手与四次挥手 - CSDN 博客
    https://blog.csdn.net/huaishu/article/details/71475007)

  • 一文搞懂 TCP 连接中的序号 seq 和确认应答号 ack!(新手向)_tcp seq 和 ack-CSDN 博客
    https://blog.csdn.net/qq_65052774/article/details/136769979

  • 书上没讲的,我来讲!
    https://mp.weixin.qq.com/s/bSo0y7mUc0oigQVMeC04Rw

  • 『面试问答』:TCP 序列号和确认号是如何变化的?
    https://mp.weixin.qq.com/s/V_82f_TlJPzINKqgUZAM4g

  • TCP 的核心组件(上):说说 TCP 序列号的运动规律
    https://mp.weixin.qq.com/s/oAPtHZOai0J9KcqwsQLOTg

  • TCP 的核心组件(下):确认号与确认机制
    https://mp.weixin.qq.com/s/jyihK_2z8ZxOZVu1oakF5g

  • Wireshark 抓包工具 --TCP 数据包 seq ack 等解读_seq ack win-CSDN 博客
    https://blog.csdn.net/wang7dao/article/details/16805337

  • 动画图解TCP三次握手过程理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)_51CTO博客_tcp三次握手初始序列号
    https://blog.51cto.com/jdsjlzx/5512125


http://www.mrgr.cn/news/95457.html

相关文章:

  • 介绍一个测试boostrap表格插件的好网站!
  • 自动驾驶系统的车辆动力学建模:自行车模型与汽车模型的对比分析
  • [学习笔记] VM虚拟机安装Ubuntu系统
  • Axure大屏可视化模板:赋能多领域,开启数据展示新篇章
  • [AI速读]混合验证方案:如何高效解决RISC-V向量扩展的验证难题
  • 文献分享: XTR——优化Token级检索的高效多向量模型
  • 【数学建模】最大最小值模型详解
  • 子集和问题---递归搜索
  • Resume全栈项目(.NET)
  • 【第22节】windows网络编程模型(WSAAsyncSelect模型)
  • 计划变动的坐标系-基线
  • 众乐影音-安卓NAS-Player的安装和设置说明
  • 蓝桥杯 之 第27场月赛总结
  • vim的一般操作(分屏操作) 和 Makefile 和 gdb
  • 浔川社团官方联合会维权成功
  • 【LeetCode 热题100】 22. 括号生成 的算法思路及python代码
  • 深入了解Spring事务及其使用场景
  • C++Primer学习(13.2 拷贝控制和资源管理)
  • PostgreSQL:数据类型与运算符
  • 单表达式倒计时工具:datetime的极度优雅(智普清言)