为何这么设计(Why's THE Design)是一系列关于计算机领域中程序设计决策的文章,咱们在这个系列的每一篇文章中都会提出一个具体的问题并从不一样的角度讨论这种设计的优缺点、对具体实现形成的影响。若是你有想要了解的问题,能够在原文下面留言。原文连接:https://draveness.me/whys-the...html
TCP 协议是咱们几乎天天都会接触到的网络协议,绝大多数网络链接的创建都是基于 TCP 协议的,学过计算机网络或者对 TCP 协议稍有了解的人都知道 —— 使用 TCP 协议创建链接须要通过三次握手(three-way handshake)。面试
若是让咱们简单说说 TCP 创建链接的过程,相信不少准备过面试的人都会很是了解,可是一旦想要深究『为何 TCP 创建链接须要三次握手?』,做者相信大多数人都没有办法回答这个问题或者会给出错误的答案,这边文章就会讨论究竟为何咱们须要三次握手才能创建 TCP 链接?网络
须要注意的是咱们会将重点放到为何须要 TCP 创建链接须要 『三次握手』,而 不只仅是为何须要 『三次』握手。
在具体分析今天的问题以前,咱们首先能够了解一下最多见的错误类比,这个对 TCP 链接过程的错误比喻误导了不少人,做者在比较长的一段时间内也认为它可以很好地描述 TCP 创建链接为何须要三次握手:less
这种用类比来解释问题每每就会面临『十个类比九个错』的尴尬局面,若是别人用类比回答你的为何,你须要仔细想想它的类比里究竟哪里有漏洞;类比带来的解释每每只能有片面的类似性,咱们永远也没法找到绝对正确的类比,它只在咱们想要通俗易懂地展现事物的特性时才能发挥较大的做用,咱们在文章的后面会介绍为何这里的类比有问题,各位读者也能够带着疑问来阅读剩下的内容。socket
不少人尝试回答或者思考这个问题的时候其实关注点都放在了三次握手中的三次上面,这确实很重要,可是若是从新审视这个问题,咱们对于『什么是链接』真的清楚?只有知道链接的定义,咱们才能去尝试回答为何 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.
RFC 793 - Transmission Control Protocol 文档中很是清楚地定义了 TCP 中的链接是什么,咱们简单总结一下:用于保证可靠性和流控制机制的信息,包括 Socket、序列号以及窗口大小叫作链接。分布式
因此,创建 TCP 链接就是通讯的双方须要对上述的三种信息达成共识,链接中的一对 Socket 是由互联网地址标志符和端口组成的,窗口大小主要用来作流控制,最后的序列号是用来追踪通讯发起方发送的数据包序号,接收方能够经过序列号向发送方确认某个数据包的成功接收。ui
到这里,咱们将原有的问题转换成了『为何须要经过三次握手才能够初始化 Sockets、窗口大小和初始序列号?』,那么接下来咱们就开始对这个细化的问题进行分析并寻找解释。this
这篇文章主要会从如下几个方面介绍为何咱们须要经过三次握手才能够初始化 Sockets、窗口大小、初始序列号并创建 TCP 链接:spa
这几个论点中的第一个是 TCP 选择使用三次握手的最主要缘由,其余的几个缘由相比之下都是次要的缘由,咱们在这里对它们的讨论只是为了让整个视角更加丰富,经过多方面理解这一有趣的设计决策。
RFC 793 - Transmission Control Protocol 其实就指出了 TCP 链接使用三次握手的首要缘由 —— 为了阻止历史的重复链接初始化形成的混乱问题,防止使用 TCP 协议通讯的双方创建了错误的链接。
The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.
想象一下这个场景,若是通讯双方的通讯次数只有两次,那么发送方一旦发出创建链接的请求以后它就没有办法撤回这一次请求,若是在网络情况复杂或者较差的网络中,发送方连续发送屡次创建链接的请求,若是 TCP 创建链接只能通讯两次,那么接收方只能选择接受或者拒绝发送方发起的请求,它并不清楚这一次请求是否是因为网络拥堵而早早过时的链接。
因此,TCP 选择使用三次握手来创建链接并在链接引入了 RST
这一控制消息,接收方当收到请求时会将发送方发来的 SEQ+1
发送回接收方,这时由发送方来判断当前链接是不是历史链接:
SEQ
过时或者超时,那么发送方就会直接发送 RST
控制消息停止这一次链接;ACK
控制消息,通讯双方就会成功创建链接;使用三次握手和 RST
控制消息将是否创建链接的最终控制权交给了发送方,由于只有发送方有足够的上下文来判断当前链接是不是错误的或者过时的,这也是 TCP 使用三次握手创建链接的最主要缘由。
另外一个使用三次握手的重要的缘由就是通讯双方都须要得到一个用于发送信息的初始化序列号,做为一个可靠的传输层协议,TCP 须要在不稳定的网络环境中构建一个可靠的传输层,网络的不肯定性可能会致使数据包的缺失和顺序颠倒等问题,常见的问题可能包括:
为了解决上述这些可能存在的问题,TCP 协议要求发送方在数据包中加入『序列号』字段,有了数据包对应的序列号,咱们就能够:
序列号在 TCP 链接中有着很是重要的做用,初始序列号做为 TCP 链接的一部分也须要在三次握手期间进行初始化,因为 TCP 链接通讯的双方都须要得到初始序列号,因此它们其实须要向对方发送 SYN
控制消息并携带本身指望的初始化序列号 SEQ
,对方在收到 SYN
消息以后会经过 ACK
控制消息以及 SEQ+1
来进行确认。
如上图所示,通讯双方的两个 TCP A/B
分别向对方发送 SYN
和 ACK
控制消息,等待通讯双方都获取到了本身指望的初始化序列号以后就能够开始通讯了,因为 TCP 消息头的设计,咱们能够将中间的两次通讯合成一个,TCP B
能够向 TCP A
同时发送 ACK
和 SYN
控制消息,这也就帮助咱们将四次通讯减小至三次。
A three way handshake is necessary because sequence numbers are not tied to a global clock in the network, and TCPs may have different mechanisms for picking the ISN's. The receiver of the first SYN has no way of knowing whether the segment was an old delayed one or not, unless it remembers the last sequence number used on the connection (which is not always possible), and so it must ask the sender to verify this SYN. The three way handshake and the advantages of a clock-driven scheme are discussed in [3].
除此以外,网络做为一个分布式的系统,其中并不存在一个用于计数的全局时钟,而 TCP 能够经过不一样的机制来初始化序列号,做为 TCP 链接的接收方咱们没法判断对方传来的初始化序列号是否过时,因此咱们须要交由对方来判断,TCP 链接的发起方能够经过保存发出的序列号判断链接是否过时,若是让接收方来保存并判断序列号倒是不现实的,这也再一次强化了咱们在上一节中提出的观点 —— 避免历史错链接的初始化。
当咱们讨论 TCP 创建链接须要的通讯次数时,咱们常常会执着于为何通讯三次才能够创建链接,而不是两次或者四次;讨论使用更多的通讯次数来创建链接每每是没有意义的,由于咱们总能够使用更多的通讯次数交换相同的信息,因此使用四次、五次或者更屡次数创建链接在技术上都是彻底能够实现的。
这种增长 TCP 链接通讯次数的问题每每没有讨论的必要性,咱们追求的实际上是用更少的通讯次数(理论上的边界)完成信息的交换,也就是为何咱们在上两节中也一再强调使用『两次握手』没有办法创建 TCP 链接,使用三次握手是创建链接所须要的最小次数。
咱们在这篇文章中讨论了为何 TCP 创建链接须要通过三次握手,在具体分析这个问题以前,咱们首先从新思考了 TCP 链接到底是什么,RFC 793 - Transmission Control Protocol - IETF Tools 对 TCP 链接有着很是清楚的定义 —— 用于保证可靠性和流控制机制的数据,包括 Socket、序列号以及窗口大小。
TCP 创建链接时经过三次握手能够有效地避免历史错误链接的创建,减小通讯双方没必要要的资源消耗,三次握手可以帮助通讯双方获取初始化序列号,它们可以保证数据包传输的不重不丢,还能保证它们的传输顺序,不会由于网络传输的问题发生混乱,到这里不使用『两次握手』和『四次握手』的缘由已经很是清楚了:
ACK
和 SYN
两个控制信息,减小了通讯次数,因此不须要使用更多的通讯次数传输相同的信息;咱们从新回到在文章开头提的问题,为何使用类比解释 TCP 使用三次握手是错误的?这主要仍是由于,这个类比没有解释清楚核心问题 —— 避免历史上的重复链接。到最后,咱们仍是来看一些比较开放的相关问题,有兴趣的读者能够仔细想一下下面的问题:
若是对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的缘由,能够在博客下面留言,做者会及时回复本文相关的疑问并选择其中合适的主题做为后续的内容。