为什么TCP建立连接需要三次握手

994 阅读7分钟

​TCP 协议是我们几乎每天都会接触到的网络协议,绝大多数网络连接的建立都是基于 TCP 协议的,学过计算机网络或者对 TCP 协议稍有了解的人都知道 —— 使用 TCP 协议建立连接需要经过三次握手(three-way handshake)。

如果让我们简单说说 TCP 建立连接的过程,相信很多准备过面试的人都会非常了解,但是一旦想要深究"为什么 TCP 建立连接需要三次握手?",作者相信大多数人都没有办法回答这个问题或者会给出错误的答案,这边文章就会讨论究竟为什么我们需要三次握手才能建立 TCP 连接?

概述

首先我们都知道握手是为了建立连接,那么为什么必须得三次握手,两次握手行不行?

Client ------SYN-----> Server
Client <---ACK/SYN---- Server
Client ------ACK-----> Server

为什么不只是这个?

Client ------SYN-----> Server
Client <-----ACK------ Server

所以我们需要将握手分解为它真正在做什么。

TCP 是一种双向通信协议,这意味着任何一端都应该能够可靠地发送数据。双方都需要建立一个序列号,双方都需要确认对方的序列号。

那么client要与server建立TCP连接,需要通过握手确认这四件事情

1.server需要确认它可以从client接收数据包;

2.client需要确认它可以从server接收数据包;

3.client需要确认一件事:server可以从client接收数据包;

4.server需要确认一件事:client可以从server接收数据包。

我们再回到“那么为什么必须得三次握手,两次握手行不行?”这个问题。

Client ------SYN-----> Server,事情1 得到确认。

Client <---ACK/SYN---- Server,事情2 和 3 得到确认。

因此,需要第三次握手来确认事情4,即

Client ------ACK-----> Server

到这里,我们已经知道了为什么建立TCP连接需要三次握手。

但是我们从TCP连接是什么的角度去审视这个问题的话,又该如何解释?我们先参考

The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream. The combination of this information,including sockets, sequence numbers, and window sizes, is called a connection.

文档中非常清楚地定义了 TCP 中的连接是什么,我们简单总结一下:用于保证可靠性和流控制机制的信息,包括 Socket、序列号以及窗口大小叫做连接。

所以,建立 TCP 连接就是通信的双方需要对上述的三种信息达成共识,连接中的一对 Socket 是由互联网地址标志符和端口组成的,窗口大小主要用来做流控制,最后的序列号是用来追踪通信发起方发送的数据包序号,接收方可以通过序列号向发送方确认某个数据包的成功接收。

到这里,我们将原有的问题转换成了“为什么需要通过三次握手才可以初始化 Sockets、窗口大小和初始序列号?“,那么接下来我们就开始对这个细化的问题进行分析并寻找解释。

设计

所以接下来我们主要讨论以下几个方面,进行解释“为什么需要通过三次握手才可以初始化 Sockets、窗口大小和初始序列号?“

1.通过三次握手才能阻止重复历史连接的初始化;

2.通过三次握手才能对通信双方的初始序列号进行初始化;

3.讨论其他次数握手建立连接的可能性。

阻止重复历史连接的初始化

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

文档中指出了 TCP 连接使用三次握手的首要原因 —— 为了阻止历史的重复连接初始化造成的混乱问题,防止使用 TCP 协议通信的双方建立了错误的连接。

在网络状况差或者复杂时,如果一个已经发送的连接请求在超时时间内没有收到确认,发送方连续发送多次建立连接的请求。想象一下,如果TCP建立连接仅有两次握手,那么双方会建立错误的连接,因为接收方接收到连接请求时并不清楚这是由于网络拥塞而早已过期的连接。

所以,TCP 选择使用三次握手来建立连接并在连接引入了 RST 这一控制消息,接收方当收到请求时会将发送方发来的 SEQ+1 发送给对方,这时由发送方来判断当前连接是否是历史连接:

1.如果当前连接是历史连接,即 SEQ 过期或者超时,那么发送方就会直接发送 RST 控制消息中止这一次连接;

2.如果当前连接不是历史连接,那么发送方就会发送 ACK 控制消息,通信双方就会成功建立连接。

使用三次握手和 RST 控制消息将是否建立连接的最终控制权交给了发送方,因为只有发送方有足够的上下文来判断当前连接是否是错误的或者过期的,这也是使用三次握手建立TCP连接的最主要原因。

对通信双方的初始序列号进行初始化

另一个使用三次握手的重要的原因就是通信双方都需要获得一个用于发送信息的初始化序列号,作为一个可靠的传输层协议,TCP 需要在不稳定的网络环境中构建一个可靠的传输层,网络的不确定性可能会导致数据包的缺失和顺序颠倒等问题,常见的问题可能包括:

1.数据包被发送方多次发送造成数据的重复;

2.数据包在传输的过程中被路由或者其他节点丢失;

3.数据包到达接收方可能无法按照发送顺序。

为了解决上述这些可能存在的问题,TCP 协议要求发送方在数据包中加入“序列号“字段,有了数据包对应的序列号,我们就可以:

1.接收方可以通过序列号对重复的数据包进行去重;

2.发送方会在对应数据包未被 ACK 时进行重复发送;

3.接收方可以根据数据包的序列号对它们进行重新排序。

序列号在 TCP 连接中有着非常重要的作用,初始序列号作为 TCP 连接的一部分也需要在三次握手期间进行初始化,由于 TCP 连接通信的双方都需要获得初始序列号,所以它们其实需要向对方发送 SYN 控制消息并携带自己期望的初始化序列号 SEQ,对方在收到 SYN 消息之后会通过 ACK 控制消息以及 SEQ+1 来进行确认。

通信次数

当我们讨论 TCP 建立连接需要的通信次数时,我们经常会执着于“为什么通信三次才可以建立连接,两次行不行?”,我们不会讨论“四次、五次握手行不行?”。增加 TCP 连接通信次数的问题往往没有讨论的必要性,我们追求的其实是用更少的通信次数(理论上的边界)完成信息的交换,也就是为什么一再强调使用“两次握手“没有办法建立 TCP 连接,使用”三次握手“是建立连接所需要的最小次数。

总结

我们重新回到在文章开头提的问题,为什么建立TCP连接需要三次握手?经过分析,答案非常清晰了。

1.防止使用 TCP 协议通信的双方建立了错误的连接;

2.减少通信双方不必要的资源消耗;

3.帮助通信双方获取初始化序列号,它们能够保证数据包传输的不重不丢,还能保证它们的传输顺序。

不使用两次握手和四次握手的原因是:

1.两次握手,无法避免历史错误连接的初始化,浪费接收方的资源;

2.四次握手,TCP 通信协议的设计可以让我们同时传递 ACK 和 SYN 两个控制信息,减少了通信次数,所以不需要使用更多的通信次数传输相同的信息。

参考资料

[^1]Why do we need a 3-way handshake? Why not just 2-way?

networkengineering.stackexchange.com/questions/2…

[^2]rfc793

datatracker.ietf.org/doc/html/rf…

[^3]为什么建立TCP连接需要三次握手

draveness.me/whys-the-de…

[^4]计算机网络-传输层

github.com/CyC2018/CS-…