TCP是面向链接的(connection-oriented),即收发双方在发送数据以前,必须首先创建一个链接,这样在链接断开以前,就一直使用这个链接传输数据。创建链接包括参数的设置、内存空间的分配,收发双方参数的协商等,这一过程须要通过三次成功的沟通,通常叫作“三次握手” (a three-way handshake)。web
用通俗的话来说,这三次沟通就是:缓存
固然,在具体的实现过程当中,还包含许多细节,如下一一阐述。服务器
要了解三次握手的过程当中发送了什么报文,首先得知道TCP报文段由哪些字段构成,其中哪些字段在这个过程当中起了关键做用。
咱们重点关注如下几个字段:cookie
接下来更为仔细地观察一条TCP链接是如何创建的。
假设运行在一台主机(客户)上的进程想与另外一台主机(服务器)上的进程创建一条链接。客户中的TCP会以以下的方式与服务器中的TCP创建一条TCP链接:
网络
在上面的讨论中咱们知道,服务器收到一个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,是合法的,因而建立一个链接。
首先,为何是三次握手而不是四次或者更多?这个问题是比较简单的,由于既然三次可以解决的问题,为何非要用四次来浪费资源?
但其实问题的重点在于,为何不能只用两次?第三次握手去掉不行吗?
对于应对SYN洪泛攻击的改进版的“三次握手”来讲(见上文),第三次握手确定是必须的,这个显而易见。
那若是不考虑攻击呢?两次握手就能搞定吗?
谢希仁版《计算机网络》对这个问题进行了讨论。总的来讲,三次握手是为了防止当已失效的链接请求报文段忽然又传到服务端,形成双方的不一致,致使资源的浪费。
“已失效的链接请求报文段”指的是这样的状况,客户端发出一个SYN报文段,因为阻塞或者其余缘由在网络中滞留,以致于客户端认为丢包了(其实并无丢),因而从新发出一个SYN报文段,假设这一次顺利完成了,那么双方创建链接。这看起来彷佛没什么问题,但网络中有一个隐患,就是那个还在网络中传输的SYN报文段,若是这个SYN在链接期间被服务端收到了,那服务端只会无视它,这样就万事大吉了,但若是是在链接释放以后被收到呢?此时服务端认为有人向他发出链接请求,因而响应一个SYNACK回去,若是采用两次握手的话,那么服务器认为此时链接已经创建好了。可是当客户端收到这个SYNACK时,若是他并无发起链接,那么他不会理睬这个SYNACK,就当没事发生过(若是客户端此时正好发起链接,那其实他也不会理睬这个SYNACK,由于确认号不对啊。)。那问题就大了,这时候服务器觉得链接好了,向客户端发送数据,而客户端处于CLOSED状态,会丢弃这些包,这样就很浪费了。而且还有一个尴尬的问题,就是这个时候当客户端打算发起链接时,服务端又不理睬了,在这里尬这,他们就别想互发数据了。固然这些问题彷佛不是不可解决的,当客户端发现服务端总是向本身发数据,而本身老是丢弃,可能会向服务端发一个RST(报文段的RST标记号为1),强制服务端关闭链接。但资源总归是浪费了一会了。而用三次握手就不会出现这样的问题。
相关阅读:
计算机网络——TCP四次挥手过程详解
TCP链接的三次握手四次挥手——类比异地恋情侣开始交往和分手(通俗易懂)