TCP建立连接的三次握手,四次挥手

2,875 阅读13分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

三次握手

三次握手其实是指建立一个TCP连接时,需要客户端和服务端一共发送三个包

1. 三次握手的主要作用

  • 确认客户端和服务端的接受和发送能力是否正常;
  • 指定自己的初始化序列号为后面的可靠性传输做准备;

其实就是连接服务器的指定端口,建立TCP连接,并且同步双方的序列号和确认号,交换TCP窗口大小的信息

2. 三次握手的过程

请添加图片描述

初始==客户端处于Close==状态,==服务端处于Listen状态==,进行三次握手

2.1 第一次握手

客户端向服务端发送一个SYN报文,指明客户端的初始化序列号ISN,发出该报文后,==客户端处于SYN-SENT==状态(同步位SYN=1;初始序号Seq=x)

2.2 第二次握手

服务端收到客户端的SYN报文后,会以自己的SYN报文作为应答,同样指定了自己的初始化序列号ISN(s),同时将客户端的ISN+1作为ack(确认号)的值,表示自己已经收到了客户端的SYN报文,发出该报文后,==服务器处于SYN-RCVD状态==(SYN=1;ACK=1;确认号ack=x+1;Seq=y)

2.3 第三次握手

客户端收到服务端的SYN报文后,会发送一个ACK报文,同样会将服务端的ISN+1作为ack的值,表示已经收到了服务端的SYN报文,发出该报文后,==客户端处于ESTABLISHED状态==,==服务器收到ACk报文后,也处于ESTABLISHED状态==。此时,双方建立TCP连接;(确认报文段ACK=1;确认号ack=y+1;序号Seq=x+1)

初始序号是x,此时是客户端发送给服务端的第二个报文,所以在初始的状态上加1

3. 三次握手的异常处理

3.1 第一次握手丢失

如果第一次握手丢失了,客户端迟迟未收到服务端的确认(SYN-ACK)报文,那么就会触发超时重传机制,(通常第一次重传是一秒后,第二次是两秒后,第三次是四秒后...每次超时重传时间是上次的两倍,超时重传的最大次数一般是五次,当第五次重传后,会继续等待32秒,如果服务端依然没有回复,客户端会断开TCP连接)==客户端重发SYN报文,直到最大重发次数==;耗时63秒,大约一分钟左右;

3.2 第二次握手丢失

第二次握手的报文端包括SYN和ACK两个报文段:

  1. ACk报文段是对第一次握手的确认报文;
  2. SYN报文是服务端发起建立TCP连接的报文;

所以当第二次握手报文丢失,客户端迟迟未收到服务端的确认报文,==客户端会认为是自己的第一次握手(SYN报文)丢失,触发超时重传机制,所以会重发SYN报文==;其次,因为第二次握手丢失,服务端就收不到第三次握手,于是==服务端触发超时重传机制,重传SYN-ACK报文==

3.3 第三次握手丢失

如果第三次握手丢失了,==服务端迟迟未收到客户端的确认报文,会认为是第二次握手(SYN-ACK)丢失,触发超时重传机制,重发SYN-ACK报文==,知道收到第三次握手或者到达最大重传次数;

  • ACK报文不会重传

3.4 为什么需要三次握手,两次可以吗?

我们首先弄明白三次握手的目的是什么,两次可以达到同样的目的嘛?

① 第一次握手:客户端发送包,服务端收到了; : 于是服务端得出了结论:客户端的发送能力和服务端的接收能力正常;

② 第二次握手:服务端发包,客户端收到了; : 于是客户端得出结论:客户端的发送、接受能力和服务端的发送接受能力都正常;不过此时的服务端并不知道客户端的接收能力是否正常;

③第三次握手:客户端发包,服务端收到了; : 这样服务端才能得出结论:双方的发送和接受能力都是正常的;

如果采用两次握手,那么只要发出确认报文就能建立TCP连接,那么有可能出现以下情况:

客户端发出了一个SYN报文,向服务端请求连接,但是因为某种原因在网络中长时间的滞留,而客户端认为是丢失了,再次重传了SYN报文,该报文正常到达了服务端,服务端回复SYN-ACK报文,,建立连接,数据传输完毕后,释放掉了连接,而此时,第一个SYN报文才姗姗来迟,到达服务端,服务端认为是客户端发起的新的连接请求,回复SYN-ACK报文,建立连接等待客户端发送数据,但是,客户端会忽略SYN-ACK报文,也不发送数据,而服务端一直等待客户端发送数据,浪费资源!

3.5 半连接队列

服务端第一次收到客户端的SYN报文,就会处于SYN-RCVD状态,此时双方还没有完全建立TCP连接,服务器会将这种状态下的请求连接放到一个队列中,称之为半连接队列, 相应的,完成了三次握手,建立了TCP连接的就会放在全连接队列中,队列满了可能会出现丢包现象.

3.6 ISN是固定的吗?

不是,他是动态生成的。

  • 每个连接都有不同的ISN,ISN可以看做是一个32比特的计数器;
  • 这样选择序号的目的是防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对他做出错误的解释;
  • 如果ISN是固定的,攻击者很容易猜出后续的确认号,因此ISN一定是动态生成的,随时间变化的;

3.7 三次握手的过程中能携带数据吗?

第一次握手和第二次握手不能携带数据,但是第三次可以携带数据;

假如第一次握手可以携带数据,那么有人恶意攻击服务器就可以在第一次握手的SYN报文中假如大量的数据,攻击者不在意服务器的收发能力是否正常,只要发送大量的SYN报文,就会让服务器花费大量的时间和内存来接收处理这些报文;

所以,第一次握手不可以携带数据,其中一个简单的原因就是让服务器更容易受到攻击; 而对于第三握手来说,客户端已经处于ESTABLISHED状态,并知道服务器的收发能力都正常,所以可以携带数据也可以接受;

3.8 SYN攻击

首先我们要知道一点:==服务端的资源是第二次握手是分配的,而客户端的资源是在完成三次握手时分配的==, 所以服务端就更容易受到SYN洪泛攻击; SYN洪泛攻击就是客户端在短时间内伪造大量不存在的IP地址,并且向服务器发送SYN包,服务器则回复SYN-ACK,并且等待客户端的确认,由于源地址是伪造的,并不存在,因此服务器需要一直重发直到最大重传次数,这些伪造的SYN包也将长时间占据半连接队列,导致正常的连接请求因为半连接队列满而被丢弃,从而引起网络拥塞甚至系统的瘫痪。 SYN是一种典型的DOS/DDOS攻击

在Linux/unix系统中,可以使用系统自带的netstat命令来检测SYN洪泛攻击;

  • natstat -n -p TCP | grep SYN-ACK

常见的防御SYN攻击的方法:

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

四次挥手

建立一个TCP连接需要三次握手,终止一个连接需要经过四次挥手,这是由==TCP的半关闭造成的==,所谓的半关闭,即是指TCP提供了连接的一端在结束他的发送后还能收到来自另一端数据的能力; TCP连接的拆除需要客户端和服务端发送四个包,所以称为四次挥手。 客户端或者服务端都可以主动发起挥手动作,一般是客户端主动发起;

4. 四次挥手的过程

初始状态,==双方都处于ESTABLISHED状态==,假设客户端先发起关闭请求。 请添加图片描述

4.1 第一次挥手

客户端发送一个FIN报文,,报文中会指定一个序列号,发出该报文后,==客户端处于FIN-WAIT1状态== 即发出连接释放报文段(FIN=1;Seq=u)并停止再发送数据,主动关闭TCP连接,进入FIN-WAIT1(终止等待1)状态,等待服务端的确认;

4.2 第二次挥手

服务端收到FIN报文之后,会回复ACK报文,且把客户端的序列号加1作为确认号,表明已经收到客户端的报文了,此时==服务端处于Clase-WAIT2状态==。 即服务端收到连接释放报文后即发出确认报文段(ACK=1;确认号ack=u+1;Seq=v)服务端进入Clase-WAIT状态(关闭等待),此时的TCP连接处于半关闭状态客户端到服务端的连接释放,客户端收到服务端的确认后,进入FIN-WAIT2(终止等待2)状态,等待服务端发出连接释放报文段;

4.3 第三次挥手

如果服务端也想关闭TCP连接,和客户端的第一次挥手一样,发出FIN报文,且指定一个序列号,,此时==服务端处于LAST-ACK状态==。 即服务器端发出连接释放报文段(FIN=1;ACK=1;Seq=w;确认号ack=u+1)服务端进入LAST-ACK(最后确认)状态,等待客户端的确认;

4.4 第四次挥手

客户端收到服务端的连接释放报文段后,同样发出一个ACK报文段作为应答,且把服务端的序列号加1作为自己的ACK报文的确认号,,此时==客户端处于TIME-WAIT状态==,需要等待一段时间以确保服务端收到自己的ACK报文之后才会进入Close状态,==服务端收到ACK报文段之后就处于关闭连接(Close)状态==。 即客户端收到服务端的FIN后,对此发出确认报文段(ACK=1;Seq=u+1;ack=w+1)客户端进入TIME-WAIT(时间等待)状态,此时TCP连接未释放掉,需要经过时间等待计时器设置的时间2MSL后,==客户端才进入Close状态==。

5.四次挥手的异常处理

5.1 第一次挥手丢失

当客户端(主动关闭方)调用close函数后,就会向服务端发送FIN报文,试图与服务端断开连接; 如果FIN报文丢失,客户端迟迟未收到服务端的确认报文,就会触发超时重传机制,重传FIN报文,当重发次数超过最大重传次数时,就不在发送FIN报文,而是直接进入close状态;

5.2 第二次挥手丢失

第二次挥手是服务端发送的ACK报文,丢失之后,ACK报文是不会重传的;如果丢失了,客户端会认为是第一次挥手(FIN)丢失,触发超时重传机制,重传FIN,知道收到服务端的ACK报文或者超过最大重传次数; 当客户端收到第二次挥手后,会进入FIN-WAIT2状态,在此状态下等待服务端发送第三次挥手(FIN)报文,对于close函数关闭的连接,由于无法再发送和接受数据,所以,FIN-WAIT2状态不可以持续太久,默认是60秒,这就意味着若60秒之后还没有收到FIN报文,客户端的连接就会直接关闭;

5.3 第三次挥手丢失

第三次挥手丢失,服务端迟迟未收到客户端回复的ACk,就会触发超时重传机制,直到收到第四次挥手或者到达最大重传次数,如果依旧没有收到ACK,则直接断开连接;

5.4 第四次挥手丢失

当客户端收到服务端的第三次挥手后,就会回复ACK报文,也就是第四次挥手,同时进入TIME-WAIT状态,在Linux系统中,TIME-WAIT状态会持续60秒后才会进入关闭状态;服务端在没有收到ACK报文之前还是处于LAST-ACK状态,如果第四次挥手丢失,服务端就会重发FIN报文;

5.5 挥手为什么需要四次?

当服务端收到客户端的的SYN连接请求报文后,可以直接发送SYN+ACK报文,Ack报文是用来应答的,SYN报文是用来同步的,但是关闭连接时,当服务端收到FIN报文时,很可能并不会关闭Socket,所以只能先回复ACK报文,告诉客户端FIN收到了,只有等到服务端所有报文都发送完了,才能发送FIN报文;

5.6 2MSL等待状态

TIME-WAIT状态也称为2MSL等待状态,每个具有TCP实现必须选择一个报文最大生存时间MSL(maximun segment lifetime)它是任何报文段被丢弃在网络内的最长时间,因为TCP报文段以IP数据报在网络中传输,而IP数据报则有限制其生存时间的TTL字段; 对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME-WAIT状态停留的时间为2倍的MSL,这样可以让TCP再次发送最后的ACK,以防止这个ACK丢失(另一端超时并重发最后的FIN) 在等待时间内,定义该连接的插口(客户端和服务端的IP地址和端口号)不能被使用,只有等待2MSL后才能再次被使用;

5.7 等待2MSL的意义

为了保证客户端发送的最后一个ACK报文能够到达服务端,因为该ACK报文有可能丢失,从而导致处在LSAT-ACK状态的服务端收不到对FIN的确认报文,服务端会超时重传FIN报文,客户端再重传一次确认,并且重置等待计时器,如果客户端不等待2MSL,而是在发送完ACK后直接关闭,一旦这个ACK丢失,服务器就无法正常关闭连接; 所以,有两个目的:

  • 保证客户端发送的ACK报文段能够到达服务端
  • 防止“已失效的连接请求报文段”出现在新的连接中 →客户端在发送完最后一个ACK报文段后,再等待2MSL,就可以使本连接持续时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现旧的请求报文段;