伤脑筋的TCP的三次握手和四次挥手

1. 三次握手

三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,并同步连接双方的***和确认号,交换TCP窗口大小信息


刚开始客户端处于Closed的状态,服务端处于Listen状态。
进行三次握手:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化*** ISN。此时客户端处于 STN_SENT 状态。
    首部的同步位 SYN = 1,初始化序号 seq = x,SYN = 1 的报文段不能携带数据,但要消耗掉一个序号。
  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化*** ISN(s)。同时会把客户端的 ISN + 1 作为ACK的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态
    在确认报文段中 SYN = 1,ACK = 1,确认号 ack = x + 1,初始序号 seq = y。
  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文。此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立了连接。
    确认报文段 ACK = 1,确认号 ack = y + 1,序号 seq = x + 1(初始为 seq = x,第二个报文段所以要 +1),ACK报文段可以携带数据,不携带数据则不消耗序号

发送第一个 SYN 的一端将执行主动打开 (active open),接收这个 SYN 并发回下一个 SYN 的另一端执行被动打开 (passive open)


在socket编程中,客户端执行 connect() 时,将触发三次握手
伤脑筋的TCP的三次握手和四次挥手

【三次握手的特点】

  1. 没有应用层的数据
  2. SYN 这个标志位只有在 TCP 建立连接时才会被置1
  3. 握手完成后 SYN 标志位被置0

1.1 为什么需要三次握手,两次不行吗?

要想弄清楚这个问题,就需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

  • 第一次握手:客户端发送网络包,服务端收到了。
    这样服务端就能得出结论:客户端的发送能力,服务端的接受能力是正常的。
  • 第二次握手:服务端发包,客户端收到了。
    这样客户端就能得出结论:客户端的发送能力和接受能力,服务器端的发送能力和接受能力是正常的。
  • 第三次握手:客户端发包,服务端收到了。
    这样服务端就能得出结论:客户端的发送和接受能力,服务端的发送和接受能力都是正常的。

因此,需要三次握手才能确认双方的接收和发送能力是否正常。

试想一下,如果是两次握手,则会出现下面这种情况:

如果客户端发送请求,但因连接请求报文丢失而未收到确认。于是客户端又发了一次连接请求,后来收到了服务端的确认,此时客户端和服务端就建立了连接,在数据传输完毕后,就释放了连接。在此过程中,客户端共发了两个请求连接报文段,其中一个丢失了,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络节点长时间滞留了,延误到连接释放以后的某个时间到达了服务端,此时服务端又认为客户端发送了一个新的连接请求,于是服务端发送确认报文,此时客户端和服务端又成功建立了连接,此时客户端认为自己并没有发起新的连接请求,所以会忽略服务端发来的确认,也不发送数据,则服务端一直等待客户端发送数据,这就会造成资源的浪费。


1.2 什么是半连接队列?

  • 半连接队列
    服务器第一次收到客户端的 SYN 之后,就会处于 SYN_SEND 状态,此时双方还未建立真正的连接,服务器会把这种状态下的请求连接放进一个队列中,我们把这种队列称之为半连接队列。
  • 全连接队列
    已经完成三次握手,建立起连接就会放到全连接队列中。如果队列满了就有可能会出现丢包现象。

【补充说明 SYN-ACK 重传次数的问题】
服务器发送完 SYN-ACK 包,如果未收到客户端确认包,服务器将进行首次重传等待一段时间仍未收到客户端确认包,进行第二次重传。如果重传次数大于最大重传次数,系统将该连接信息从半连接队列中删除。
【注意】每次重传等待的时间不一定相同,一般会指数增长,例如间隔时间为 1s,2s,4s,8s……

1.3 ISN(Initial Sequence Number)是固定的吗?

  • 当一端为建立连接而发送它的 SYN 时,它为连接选择一个初始序号。ISN 随时间变化,因此每个连接都将具有不同的 ISN。ISN可以看做一个32比特的计数器,每 4ms 加 1。这样选择序号的目的在于防止网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它错误的解释。
  • 三次握手的其中一个重要的功能是客户端和服务端交换 ISN,以便让对方知道接下来接收数据的时候如何按***组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

1.4 三次握手过程可以携带数据吗?

其实第三次握手是可以携带数据的。但是,第一次、第二次握手绝对不可以携带数据

【原因】
大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次五首的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂重复发着 SYN 报文的话,会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不仅可以放数据,其中一个很简单的原因就是不会让服务器更容易受到攻击。而对于第三次的话,此时客户端已处于 ESTABLISHED状态。对于客户端来说,它已经建立了连接,并且已经知道 服务端的发送和接受能力都是正常的,所以能携带数据也没啥毛病。