先决条件知识
首先,让我们看一下TCP的控制位和状态机,它们构成了理解TCP三次握手的基础。
TCP数据包控制位
TCP数据包头中的控制位用于控制TCP连接的状态,可以指示各种控制信息,例如连接建立、终止、重置等。
有六个常见的控制位:
- SYN(同步序列号):请求建立连接(三次握手的一部分)。它在连接建立期间的初始数据包中设置,表示发送方希望建立连接并同步序列号。由于TCP是双向的,因此双方在建立连接时都需要发送SYN。虽然SYN数据包不能携带数据,但它们确实会消耗一个序列号。
- ACK(确认字段有效):确认已接收的数据。当设置ACK标志时,接收方会在确认字段中填写下一个预期的序列号。不携带数据的ACK数据包不会消耗序列号。
- FIN(发送方不再有数据):请求终止连接(四次挥手的部分)。它在发送所有数据后在数据包中设置,通知接收方发送方已完成所有数据的发送。由于TCP是双向的,因此双方在关闭连接时都需要发送FIN。虽然FIN数据包不携带数据,但它们确实会消耗一个序列号。
- RST(重置连接):重置连接,用于异常或错误的连接终止。当接收方收到RST标志时,它会立即终止连接,而无需任何数据确认。例如,前面文章中提到的TIME_WAIT问题。注意:生产环境中出现RST数据包通常表示潜在问题。
- PSH(推送功能):指示接收方应立即将接收到的数据传递到应用程序层,建议应快速处理数据,而不是等待更多后续数据。
- URG(紧急指针字段有效):指示数据具有高优先级,应尽快由接收方处理。
- ECE(ECN-Echo):指示双方是否在三次握手期间协商了对显式拥塞通知的支持。
序列号 (Seq)
由于TCP是双向的,单个连接的双方都可以互相发送数据,因此每一侧都必须维护自己的Seq字段。
Seq是动态且随机生成的,这有助于防止伪造的数据包重置连接(RST攻击)。
TCP提供有序传输,因此每个数据段都必须包含一个Seq编号字段:
- 当接收方收到乱序的数据包时,它可以根据Seq重新排序。
- 当接收方收到重复的数据包时,它可以根据Seq去除重复。
如图所示,序列号的增量方法(重要):
- 对于段1,如果起始Seq编号为1,长度为1448字节,则段2的Seq编号将为1 + 1448 = 1449。
- 如果段2的长度也是1448,则段3的Seq编号将为1449 + 1448 = 2897。
换句话说,Seq编号的大小是通过将Seq编号和前一段的长度相加得出的。
Seq = 前一段的Seq + 长度 (Len)
因此,在TCP数据传输过程中,任何一方发送的段都应该是连续的:下一个数据包的Seq编号等于前一个数据包的Seq + 长度(三次握手和四次挥手除外)。
Len(段长度)
需要注意的是,长度(Len)不包括TCP头的长度;因此,不要认为Len = 0
的数据包毫无意义。TCP头本身携带大量信息。
ACK(确认号)
接收方告诉发送方它已接收哪些段(Seq编号)。
- 如果发送方向接收方发送一个Seq:1和Len:100的段,则接收方返回的ACK将为1 + 100 = 101,表示它已接收Seq 101之前的所有数据。
- 如果发送方向接收方发送一个Seq:101和Len:50的段,则接收方返回的ACK将为101 + 50 = 151,表示它已接收Seq 151之前的所有数据。
例如,如果A方向B方发送一个“Seq:x Len:y”的段,则B方的ACK响应将为x + y,这意味着它已接收x + y
之前的所有字节。
结论:接收方发回的ACK编号正好等于发送方期望发送的下一个Seq编号。因此,我们可以看到数据包10377的ACK编号正好等于数据包10378的Seq编号。
如果双方在通信过程中都没有发送任何数据,则另一方返回的ACK编号将不会改变(即它在三次握手期间保持初始值)。
TCP状态机
以下是经典的TCP状态机图,它涵盖了TCP连接从初始建立到最终终止在其整个生命周期中的所有状态。
这是维基百科上的彩色版本:
三次握手示例
下面是一个典型的图,说明了三次握手。
- 第一次握手:客户端发起连接请求,将SYN位设置为1,并初始化一个随机序列号(ISN)Seq = x。发送SYN数据包后,客户端进入SYN-SENT状态。
- 第二次握手:服务器收到客户端的连接请求后,初始化一个随机序列号(ISN)Seq = y,将SYN和ACK位设置为1,并将ACK编号设置为x + 1。发送SYN-ACK数据包后,服务器进入SYN-RECEIVED状态。
- 第三次握手:客户端收到服务器的响应后,将ACK位设置为1并发送ACK数据包。然后客户端进入ESTABLISHED状态。服务器收到ACK编号为y + 1的ACK数据包后,也进入ESTABLISHED状态,完成TCP连接建立。
在连接过程中,客户端和服务器的状态转换如下:
从图中可以看出,对于ESTABLISHED(连接已建立)状态,客户端和服务器感知连接完成的时间不同:
- 对于客户端,在第二次握手后连接被认为已建立。
- 对于服务器,在第三次握手后连接被认为已建立。
Wireshark数据包捕获
为了更直观地理解TCP三次握手过程,我们使用Wireshark进行数据包捕获:
打开Wireshark并开始捕获数据包,然后在终端中执行以下命令:
curl -I -H "Connection: close" https://dbwu.tech
切换到Wireshark界面并使用TCP过滤来查看数据包。您可以观察到TCP三次握手过程,其中192.168.3.68是LAN内的本地IP地址。
- 第一次握手:客户端使用Seq字段设置为3123802190发起连接请求。
- 第二次握手:服务器使用Seq字段设置为1071295171,ACK = x + 1,即3123802191进行响应。
- 第三次握手:客户端使用ACK = y + 1,即1071295172进行响应。
提示:默认情况下,Wireshark显示相对Seq值(从0开始)。如果您想查看客户端和服务器的实际随机Seq值,可以在Wireshark的设置中进行调整:
编辑 - 首选项 - 协议 - TCP
取消选中:相对序列号
证明(粗略版本)
在解释TCP三次握手的理论基础之后,我们可以分析并证明命题:
建立TCP连接至少需要三次握手。
使用一种称为反证法的基本数学证明方法,我们假设如下:
TCP连接可以在没有三次握手的情况下建立。
具体来说,我们假设TCP可以用少于三次握手来建立连接,用N表示。我们将N分成三个范围:
N < 1 | N == 1 | N == 2
我们将分别证明每种情况。
1. N < 1
当N < 1时,这意味着没有人发起第一次握手。在这种情况下,双方都不知道对方的存在,因此无法建立连接。因此,N < 1是无效的,这导致我们下一个假设:N == 1。
2. N == 1
当N == 1时,这意味着只发生一次握手,即发送方向接收方发送连接请求(SYN)。但是,请求发送后没有进一步的操作,因此无法建立连接,因为:
- 发送方无法确认其传输功能是否正常工作(是否可以正常向接收方发送数据)。
- 接收方无法确认其接收功能是否正常工作(包括是否在正确的端口上监听以及其缓冲区是否正常工作)。
如果没有这些确认,发送方会认为连接未建立,并且不会继续发送数据。因此,N == 1是无效的,这导致我们下一个假设:N == 2。
3. N == 2
当N == 2时,这意味着发生两次握手。基于N == 1的证明,我们继续我们的检查。
第一次握手后,接收方用响应(SYN-ACK)确认发送方的请求。与发送方的消息类似,接收方的响应被发送,但没有其他事情发生,因此连接仍然无法建立。现在,两件事得到了确认:
- 发送方的传输功能是正常的。
- 接收方的接收功能是正常的。
但是,接收方无法确认:
- 其传输功能是否正常工作(是否可以正常向发送方发送数据)。
- 发送方的接收功能是否正常工作。
因此,N == 2对于建立连接也不够,这表明建立TCP连接至少需要三次握手。
原始命题证明
通过前面提供的反证法,我们可以证明建立TCP连接至少需要三次握手。
根据前面概述的推理,让我们检查当N == 3时发送方和接收方的状态变化。
第二次握手后,发送方收到接收方的连接建立响应数据包(SYN-ACK),并用确认数据包(ACK)进行回复。
现在可以确认以下内容:
- 接收方的传输功能是否正常工作(是否可以正常向发送方发送数据)。
- 发送方的接收功能是否正常工作。
N == 2时出现的两个问题现在已解决:
- 接收方的传输功能是正常的。
- 发送方的接收功能是正常的。
证明(正确版本)
前面,我们使用了粗略的反证法以及TCP数据包的状态来证明建立TCP连接至少需要三次握手。此外,我们还可以使用TCP序列号(Seq)严格地证明这一点。
为了实现可靠的数据传输,TCP通信中的双方都必须维护一个序列号(Seq),以识别对方已经接收了哪些数据包。
三次握手过程包括:双方交换其初始序列号并确认对方已收到这些初始序列号。
- 如果只有一次握手,接收方不会发送其初始序列号。
- 如果只有两次握手,只能确认发送方的初始序列号,而接收方的初始序列号无法验证。
此外,只有两次握手的情况下,可能会出现由延迟数据包引起的异常情况,导致无效连接。
如图所示,网络中有多条路径。客户端发送的第一个建立连接的数据包最终可能落在路径延迟严重的地方,导致它到达服务器的时间很长。
客户端超时后,它假设第一个数据包丢失并重新启动请求。第二个请求走正常的路径,并快速完成连接。
从客户端的角度来看,通信似乎已经结束,但随后其第一个数据包延迟到达服务器。由于服务器不知道这是一个旧的无效请求,因此它会正常响应。
如果TCP只有两次握手,则会在服务器上建立无效的(已过期重复)连接。
但是,在三次握手机制下,当客户端收到服务器的响应时,它会意识到这是一个过时的连接,并用RST数据包进行响应。收到RST后,服务器关闭连接。
关键点:三次握手确认的是发送方和接收方的初始序列号(ISN,Initial Sequence Number)。此值将当前连接与历史旧连接区分开来。
结论
理论上,即使超过三次握手也不能保证TCP连接“完全可靠”。但是,通过三次握手,至少可以确认连接是“基本可用”的。增加握手的次数只会增加对“连接可用性”的信心水平。
TCP三次握手后,发送方和接收方的状态变化如下:
- 发送方确认:自身的发送和接收是正常的,对方的发送和接收也是正常的。
- 接收方确认:自身的发送和接收是正常的,对方的发送和接收也是正常的。
总而言之,TCP中的三次握手是软件工程中“权衡”的经典示例。
翻译自 https://dbwu.tech/posts/network/why-tcp-does-needs-three-way-handshake/
The 3-way handshake is not necessary. It does what it suppose to but that is not why it works. In 1978, published in 1980, Richard Watson of LLL proved that the necessary and sufficient condition for synchronization for reliable data transfer is to impose an upper bound on 3 times: MPL, Maximum Packet Life time; A, maximum time to wait before sending an Ack; and R, maximum time to exhaust retries.
The message exchange has nothing to do with it. TCP sort of works because TTL bounds MPL and the other two are bounded by the assumptions about performance and the that the TCB has to be held for 120 seconds before the port-ids can be re-used. This is less secure and less robust. Watson and team defined a protocol called delta-t, implemented it and used it at LLL for many years.
This is a hard result to wrap one's head around. It isn't that the 3-way handshake is wrong, it just has nothing to do with why it works. Any sequence of 3 or packets will have the same effect as long as the bounds are enforced.
There is much more to this result. For more information and the sources, you can contact me at day@bu.edu.
Of the 3 or 4 protocols considered in the mid-70s, TCP was by far the worst design and when there was vote, it was not chosen by a margin of 2-1.
Take care,
John Day