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

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

    在具体分析今天的问题之前,我们首先可以了解一下最常见的错误类比,这个对 TCP 连接过程的错误比喻误导了很多人,认为它能够很好地描述 TCP 建立连接为什么需要三次握手:
    image.png
    这种用类比来解释问题只有片面的相似性,它只在我们想要通俗易懂地展示事物的特性时才能发挥较大的作用你发现不了漏洞。待会我们会来讲解为什么这里的类比有问题。

    我们谈论到TCP连接中经常只记住三次握手 这确实很重要,但是我认为只有知道连接的定义,我们才能去尝试回答为什么 TCP 建立连接需要三次握手。

    image.png
    RFC 793 - 传输控制协议
    在文档中非常清楚地定义了 TCP 中的连接是什么,我们简单总结一下:用于保证可靠性和流控制机制的信息,包括 Socket、序列号以及窗口大小叫做连接。
    为什么 TCP 建立连接需要三次握手 - 图3
    所以,建立 TCP 连接就是通信的双方需要对上述的三种信息达成共识。

    • Socket 是由互联网地址标志符和端口组成的。
    • 窗口大小主要用来做流控制。
    • 最后的序列号是用来追踪通信发起方发送的数据包序号,接收方可以通过序列号向发送方确认某个数据包的成功接收。

    那为什么需要通过三次握手才可以初始化 Sockets、窗口大小、初始序列号并建立 TCP 连接:

    在《RFC 793 - 传输控制协议 》中可以找到答案它里面说到了首要原因,为了阻止历史的重复连接初始化造成的混乱问题,防止使用 TCP 协议通信的双方建立了错误的连接。

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

    想象一下这个场景,如果通信双方的通信次数只有两次,那么发送方一旦发出建立连接的请求之后它就没有办法撤回这一次请求,如果在网络状况复杂或者较差的网络中,发送方连续发送多次建立连接的请求,如果 TCP 建立连接只能通信两次,那么接收方只能选择接受或者拒绝发送方发起的请求,它并不清楚这一次请求是不是由于网络拥堵而过期的连接。

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

    • 如果当前连接是历史连接,过期或者超时,那么发送方就会直接发送RST控制消息中止这一次连接;
    • 如果当前连接不是历史连接,那么发送方就会发送ACK控制消息,通信双方就会成功建立连接;

    通过这个流程大家发现没有?最终控制权交在发送方,因为只有发送方有足够的上下文来判断当前连接是否是错误的或者过期的,这也是 TCP 使用三次握手建立连接的最主要原因。

    另外网络的不确定性可能会导致数据包的缺失和顺序颠倒、重复等问题 也是要使用三次握手的重要的原因。

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

    • 接收方可以通过序列号对重复的数据包进行去重;
    • 发送方会在对应数据包未被 ACK确认号 时进行重复发送;
    • 接收方可以根据数据包的序列号对它们进行重新排序;

    为什么 TCP 建立连接需要三次握手 - 图4
    如上图所示,客户端将标志位SYN置为1,随机产生一个(序列号)值seq=100,并将该数据包发送给服务器端等待服务器端确认。

    服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,随即他做了两个事情
    一:把连接成功的消息 ack = 100+1 以及接收的序列号返回给客户端。
    二: 随机产生一个值seq=K(序列号),并将数据包发送给客户端确认连接请求。

    客户端收到服务端确认消息后,双方检测序列号是否正确,正确则连接建立成功,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

    由于 TCP 消息头的设计,我们可以将中间的两次通信合成一个,这也就帮助我们将四次通信减少至三次。

    当我们讨论 TCP 建立连接需要的通信次数时,我们经常会执着于为什么通信三次才可以建立连接,而不是两次或者四次;使用四次、五次或者更多次数建立连接在技术上都是完全可以实现的。

    这种增加 TCP 连接通信次数的问题往往没有讨论的必要性,我们追求的其实是用更少的通信次数(理论上的边界)完成信息的交换,也就是为什么我们在上两节中也一再强调使用『两次握手』没有办法建立 TCP 连接,使用三次握手是建立连接所需要的最小次数。