三次握手以及四次挥手

TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接。

★★★ 理解三次握手以及四次挥手具体过程,三次握手的原因、四次挥手原因、TIME_WAIT 的作用

参考连接

面试官,不要再问我三次握手和四次挥手

TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接 TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。

TCP连接建立过程中使用的信号量

SYN:同步序列编号(Synchronize Sequence Numbers),表示建立连接。

ISN:初始化序列号 (Initial Sequence Number)也称 seq,是一个序号。

ACK:确认字符(Acknowledge character),表示响应。

FIN:结束字符(Finish),表示关闭连接。

表面上看三次握手

第一次握手

建立连接时,主机A发送SYN报文主机B,指明主机A初始化序列号 ISNx,并进入 SYN_SEND 状态,等待主机B确认。

首部的同步位 SYN=1,初始序号 seq=xSYN=1的报文段不能携带数据,但要消耗掉一个序号。

第二次握手

主机B收到主机ASYN 报文之后,会以自己的 SYN-ACK 报文作为应答,并且是指定了自己的初始化序列号seqy。同时会把客户端的 seq + 1 作为 ack 的值,表示自己已经收到了 主机ASYN报文,此时 主机B 处于 SYN_REVD 的状态。

在确认报文段中SYN=1ACK=1,确认序号 ack=x+1,初始序号seq=y

第三次握手

主机A收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把主机Bseq + 1 作为 ack 的值,表示已经收到了主机BSYN 报文,此时主机A处于 ESTABLISHED 状态。主机B收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。

确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

完成三次握手,主机A与服务器开始传送数据。

img

三次握手的意义(为什么不能是两次握手?)

第一次握手:主机A发送SYN报文主机B,主机B得出结论,主机A的发送能力、自己的接收能力是正常的。

主机A视角 主机B视角
主机A的发送能力 正常(第一次握手确认)
主机A的接收能力
主机B的发送能力
主机B的接收能力 正常(第一次握手确认)

第二次握手:主机B发送SYN报文主机A,主机A得出结论,主机B的发送能力、自己的发送、接收能力是正常的。不过此时主机B并不能确认主机A的接收能力是否正常。

主机A视角 主机B视角
主机A的发送能力 正常(第二次握手确认 正常(第一次握手确认)
主机A的接收能力 正常(第二次握手确认
主机B的发送能力 正常(第二次握手确认
主机B的接收能力 正常(第二次握手确认 正常(第一次握手确认)

第三次握手:主机A发送ACk报文主机B,主机B得出结论,主机A的接收、发送能力正常,主机的接收和发送能力也正常。

主机A视角 主机B视角
主机A的发送能力 正常(第二次握手确认) 正常(第一次握手确认)
主机A的接收能力 正常(第二次握手确认) 正常(第三次握手确认
主机B的发送能力 正常(第二次握手确认) 正常(第三次握手确认
主机B的接收能力 正常(第二次握手确认) 正常(第一次握手确认)

三次握手的实质:相互印证主机A和主机B的发送、接收能力正常。

三次握手中的其他问题

什么是半连接队列?

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。

当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

这里在补充一点关于SYN-ACK 重传次数的问题:

服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。

注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

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

当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN。ISN可以看作是一个32比特的计数器,每4ms加1 。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。

三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

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

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

为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。

也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

SYN攻击是什么?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

1
netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:

  • 缩短超时(SYN Timeout)时间
  • 增加最大半连接数
  • 过滤网关防护
  • SYN cookies技术

表面上看四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

第一次挥手

主机A会发送一个 FIN 报文主机B,报文中会指定一个序列号 seq=u 。此时客户端处于 FIN_WAIT1 (终止等待1)状态。

第二次挥手

主机B收到 FIN报文 之后,会发送 ACK 报文,且把主机Aseq +1 作为 ACK 报文的序列号值,表明已经收到主机A的报文了,此时主机B处于 CLOSE_WAIT(关闭等待)状态。

主机B收到FIN 报文后即发出ACK报文ACK=1,确认号ack=u+1,序号seq=v),主机B进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,主机A主机B的连接释放。主机A收到主机B的确认后,进入FIN_WAIT2(终止等待2)状态,等待主机B发出的连接释放报文段。

第三次挥手

如果主机B也想断开连接了,和主机A的第一次挥手一样,发给 FIN-ACK 报文,且指定一个序列号 seq=w。此时主机B处于 LAST_ACK 的状态。

主机B没有要向主机A发出的数据,主机B发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),主机B进入 LAST_ACK(最后确认)状态,等待主机A的确认。

第四次挥手

主机A收到 FIN报文 之后,一样发送一个 ACK 报文作为应答,且把主机B的序列号值 +1 作为自己 ACK 报文的序列号值seq=u+1,此时主机A处于 TIME_WAIT 状态。需要过一阵子以确保主机B收到自己的 ACK 报文之后才会进入 CLOSED 状态,主机B收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

主机A收到主机B的连接释放报文段后,对此发出确认报文段(ACK=1seq=u+1ack=w+1),主机A进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,主机A才进入CLOSED状态。

收到一个FIN报文只意味着在这一方向上没有数据流动。主机A执行主动关闭并进入TIME_WAIT是正常的,主机B通常执行被动关闭,不会进入TIME_WAIT状态。

在socket编程中,任何一方执行close()操作即可产生挥手操作。

客户端为主机A,服务端为主机B。

img

四次挥手的意义(为什么不能是三次挥手)

因为当主机B收到主机ASYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当主机B收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉主机A,“你发的FIN报文我收到了”。只有等到我主机B所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

四次挥手中的其他问题

MSL等待状态

TIME_WAIT状态也成为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。

对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。

这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的套接字(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。

四次挥手释放连接时,等待2MSL的意义?

MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。

两个理由:

  • 保证客户端发送的最后一个ACK报文段能够到达服务端。

这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后立即释放连接,则无法收到服务端重传的FIN+ACK报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到CLOSED状态。

  • 防止“已失效的连接请求报文段”出现在本连接中。

客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。

2.4 为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSE状态?

理论上,四个报文都发送完毕,就可以直接进入CLOSE状态了,但是可能网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

总结

img

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!