计算机网络——TCP三次握手详解为何两次不行

TCP是面向链接的(connection-oriented),即收发双方在发送数据以前,必须首先创建一个链接,这样在链接断开以前,就一直使用这个链接传输数据。创建链接包括参数的设置、内存空间的分配,收发双方参数的协商等,这一过程须要通过三次成功的沟通,通常叫作“三次握手” (a three-way handshake)。web

用通俗的话来说,这三次沟通就是:缓存

  • 发起方:“你好,请问我能够跟你创建一个链接吗?”(发送请求,等待回复)
  • 接收方:“好啊,我准备好了,来吧。”
  • 发起方:“好的,谢谢,我如今开始向你发送数据了。”

固然,在具体的实现过程当中,还包含许多细节,如下一一阐述。服务器

1. TCP报文段结构

要了解三次握手的过程当中发送了什么报文,首先得知道TCP报文段由哪些字段构成,其中哪些字段在这个过程当中起了关键做用。
在这里插入图片描述
咱们重点关注如下几个字段:cookie

  • Sequence number:序列号,用来标记一个报文段的序号,报文段首字节的字节流编号
  • Acknowledgment number:确认号,只有ACK标志位为1时,确认序号字段才有效
  • ACK:用于指示确认号是有效的
  • SYN:同步序列编号(Synchronize Sequence Numbers)

2. 三次握手

接下来更为仔细地观察一条TCP链接是如何创建的。
假设运行在一台主机(客户)上的进程想与另外一台主机(服务器)上的进程创建一条链接。客户中的TCP会以以下的方式与服务器中的TCP创建一条TCP链接:
在这里插入图片描述网络

  • 第一步:客户端的TCP向服务器端的TCP发送一个特殊的TCP报文段,这个报文段不包含应用层数据,且其首部的SYN被置为1,这个特殊的报文段称为SYN报文段。而且,客户机随机选择一个初始序号(client_isn, initial sequence number),并将该值放在序列号字段下。客户端发送SYN报文段,并进入SYN_SENT状态,等待服务器确认。
  • 第二步:一旦服务端收到该TCP SYN报文段(从SYN标记位为1能够判断),会为该链接分配TCP缓存(buffers)和变量,并发送一个容许链接的报文段 (connection-granted segment)给客户TCP。这个报文段也不包含应用层数据,而且SYN一样被置为1,此外ACK标记位也为1,确认号为client_isn+1,而且选择一个初始序号server_isn做为序列号字段的值。这个报文段被称为SYNACK报文段。此时服务器进入SYN_RECV状态。
  • 第三步:客户端收到SYNACK报文段(经过其中的SYN,ACK,确认号能够判断)以后,也为该链接分配缓存和变量。而后客户机会再次向服务端发送一个确认报文,ACK标记位为1,确认号为server_isn+1,(此次SYN为0,SYN只在前两次握手中置为1),此次的报文段能够携带来自应用层的数据。此时客户端进入ESTABLISHED状态。服务端收到这个报文段后,也进入ESTABLISHED状态,此时链接就算彻底创建好了,双方能够相互发送数据。

3. SYN洪泛攻击

在上面的讨论中咱们知道,服务器收到一个SYN报文段时,分配并初始化链接变量和缓存,而后发送一个SYNACK进行响应。在收到来自客户端的ACK报文段以前,链接并无彻底创建,咱们称它为半开链接 (half-open connection)。若是客户不发送ACK以完成三次握手的第三步,那么服务器会在必定时间内终止该半开链接,并回收分配的资源。并发

在这样的协议下,很容易被一种叫作SYN洪泛攻击 (SYN flood attack) 的拒绝服务攻击 (Denial of Service (DoS) attack) 侵袭。攻击者向服务器发送大量的TCP SYN报文段,而不完成三次握手的第三步,这样服务器不断为这些半开链接分配资源,致使服务器的链接资源消耗殆尽。svg

目前有一种防护机制能够抵御这种攻击,称为SYN cookie函数

这种机制的思想在于,在收到SYN以后不立刻进行分配资源(由于怕了),而是在第三步时判断链接的发起者是否为一个合法用户,若是是,再分配资源并创建链接。.net

首先,当服务器收到一个SYN时,不立刻分配资源,而是按以下方式生成一个初始的序列号:该序列号是 “SYN报文段中的源和目的IP地址与端口号以及一个只有服务器本身知道的秘密数 (secret number) ” 的hash值,也就是说,只有知道这个秘密数,才可能算出这个序列号(这个初始序列号就称为“cookie”)。而后服务器就发送包含这个初始序列号的SYNACK。须要注意的是,服务器此时不维护任何关于该SYN的状态信息,甚至不用记住这个cookie值。因此若是客户没有返回一个ACK,那么对服务器来讲就当什么时都没发生,如今SYN洪泛攻击就作不成了。计算机网络

那么合法用户是怎样完成第三个步骤的呢?其实并无什么改变,任然按照原来的方式进行,发送一个ACK给服务器。此时须要动点手脚的是服务端,服务端怎么判断这个ACK报文是对以前的SYNACK的确认呢?很简单,由于以前的SYNACK的序列号是根据“SYN报文段中的源和目的IP地址与端口号以及一个只有服务器本身知道的秘密数”算出来的,那么此次若是仍是那个用户的话,那么源和目的IP地址与端口号是不会变的,而后秘密数服务端也知道,用原来的hash函数一算,就得出来了该序列号,而后加1,看是否是跟这个ACK报文的确认号相等,若是相等,那说明这个ACK对应以前的SYNACK,是合法的,因而建立一个链接。

4. 为何是“三次”

首先,为何是三次握手而不是四次或者更多?这个问题是比较简单的,由于既然三次可以解决的问题,为何非要用四次来浪费资源?

但其实问题的重点在于,为何不能只用两次?第三次握手去掉不行吗?
对于应对SYN洪泛攻击的改进版的“三次握手”来讲(见上文),第三次握手确定是必须的,这个显而易见。

那若是不考虑攻击呢?两次握手就能搞定吗?

谢希仁版《计算机网络》对这个问题进行了讨论。总的来讲,三次握手是为了防止当已失效的链接请求报文段忽然又传到服务端,形成双方的不一致,致使资源的浪费。

“已失效的链接请求报文段”指的是这样的状况,客户端发出一个SYN报文段,因为阻塞或者其余缘由在网络中滞留,以致于客户端认为丢包了(其实并无丢),因而从新发出一个SYN报文段,假设这一次顺利完成了,那么双方创建链接。这看起来彷佛没什么问题,但网络中有一个隐患,就是那个还在网络中传输的SYN报文段,若是这个SYN在链接期间被服务端收到了,那服务端只会无视它,这样就万事大吉了,但若是是在链接释放以后被收到呢?此时服务端认为有人向他发出链接请求,因而响应一个SYNACK回去,若是采用两次握手的话,那么服务器认为此时链接已经创建好了。可是当客户端收到这个SYNACK时,若是他并无发起链接,那么他不会理睬这个SYNACK,就当没事发生过(若是客户端此时正好发起链接,那其实他也不会理睬这个SYNACK,由于确认号不对啊。)。那问题就大了,这时候服务器觉得链接好了,向客户端发送数据,而客户端处于CLOSED状态,会丢弃这些包,这样就很浪费了。而且还有一个尴尬的问题,就是这个时候当客户端打算发起链接时,服务端又不理睬了,在这里尬这,他们就别想互发数据了。固然这些问题彷佛不是不可解决的,当客户端发现服务端总是向本身发数据,而本身老是丢弃,可能会向服务端发一个RST(报文段的RST标记号为1),强制服务端关闭链接。但资源总归是浪费了一会了。而用三次握手就不会出现这样的问题。


相关阅读:
计算机网络——TCP四次挥手过程详解
TCP链接的三次握手四次挥手——类比异地恋情侣开始交往和分手(通俗易懂)