异常的三次握手四次挥手
介绍
TCP作为一个靠谱的协议,在传输数据的前后,需要在双端之间建立连接,并在双端各自维护连接的状态。
建立连接前,TCP会通过三次握手来保证双端状态正确,然后就可以正常传输数据了。当数据传输完成,需要断开连接的时候,TCP会通过四次握手来完成双端的断连,并回收各自的资源。
这个过程中也会产生各种问题,有因网络原因造成的也有其他原因造成的。一般来说主要就是丢包的问题,基本就是超时重传等来保证TCP的可靠性。
三次握手中的问题
客户端第一个SYN包丢失
如果客户端的第一个ACK包丢失,服务器无法收到这个包,不会做任何事情。
而在TCP协议中,某端的一组「请求-应答」中,在一定时间范围内,只要没有收到应答的「ACK」包,无论是请求包对方没有收到,还是对方的应答包自己没有收到,均认为是丢包了,都会触发超时重传机制。
此时客户端会重传丢失的SYN包。
服务端收到SYNd返回的SYN+ACK包丢失
此时服务端已经收到客户端的数据,并且发出了对于改信息的回复。
这个问题站在客户端而言,无法区分是服务端没有收到第一次的SYN包,还是自己没有收到回复信息,只能等待一段时间后重新发送SYN包,这和上面的情况就一样了。
对于服务端,发送了SYN+ACK包后,无法等到对方的确认ACK信息,也只能对这个SYN+ACK包进行重传。
由于客户端的重传,服务端会收到这个重传的SYN包,此时会针对这个新的SYN包立即发送SYN+ACK信息。
客户端收到SYN+ACK返回的ACK包丢失
如果最后一个ACK包丢了,服务端因为收不到ACK会走重传机制,而客户端此时进入ESTABLISHED状态。
多数情况下,客户端进入ESTABLISHED状态后,则认为连接已建立,会立即发送数据。但是服务端因为没有收到最后一个ACK包,依然处于SYN-RCVD状态。
所以当服务端处于SYN-RCVD状态下时,接收到客户端真实发送来的数据包时,会认为连接已建立,并进入ESTABLISHED状态。
也有地方认为当服务端处于SYN-RCVD状态下,收到客户端的数据包后,会直接回复RST包响应,表示服务端错误,并进入CLOSE状态。
实际情况下,当客户端在ESTABLISHED状态下,开始发送数据包时,会携带上一个ACK的确认序号,所以哪怕客户端响应的ACK包丢了,服务端在收到这个数据包时,能够通过包内ACK的确认序号,正常进入ESTABLISHED状态。
客户端故意不发最后一次ACK包
-0
如果客户端是恶意的,在发送SYN包后,并收到SYN+ACK后就不回复了,那么服务端此时处于一种半连接的状态,虽然服务端会通过tcp_synack_retries配置重试的次数,不会无限等待下去,但是这也是有一个时间周期的。
如果短时间内存在大量的这种恶意连接,对服务端来说压力就会很大,这就是所谓的SYN FLOOD攻击。
四次挥手中的问题
客户端断开连接的FIN包丢失
这种情况下,会触发超时重传机制,不会去考虑是自己发出的包丢失,还是无法收到对方发来的恢复,直到关闭连接。
服务端第一次返回的ACK包丢失
这种情况下,客户端无法收到服务端返回的ACK包,会触发重传机制,重传FIN包。
而服务器收到重传的FIN包后,会立即在重传对FIN包的ACK包。
而此时服务器已经进入CLOSED-WAIT状态,开始做断开连接前的准备工作。当准备好之后,会回复FIN+ACK,这个消息是携带了之前ACK的响应序号的,这就是第三次挥手的包。
服务器发送的FIN+ACK包丢失
这是第三次挥手的包丢失了,此时客户端有两种情况,要么处于FIN-WAIT-2状态(之前的 ACK 也丢了),会一直等待;要么处于TIME-WAIT状态,会等待 2MSL 时间。
而服务端则收不到对应的ACK确认包,则会进行超时重传。
客户端最后返回的ACK包丢失
客户端在回复ACK后,会进入TIME-WAIT状态,开始长达2MSL的等待,服务端因为没有收到ACK的回复,会重试一段时间,直到服务端重试超时后主动断开。
或者等待新的客户端接入后,收到服务端重试的FIN消息后,回复RST消息,在收到RST消息后,复位服务端的状态。
客户端收到ACK后服务端跑路
客户端在收到ACK后,进入了FIN-WAIT-2状态,等待服务端发来的FIN包,而如果服务端跑路了,这个包永远都等不到。
在TCP协议中,是没有对这个状态的处理机制的。但是协议不管,系统来凑,操作系统会接管这个状态,在一段时间后,直接进行客户端关闭,这中间会用心跳等进行检测。
客户端收到ACK后客户端跑路
客户端收到ACK后直接跑路,服务端后续在发送的FIN+ACK就没有接收端,也就不会得到回复,会不断的走TCP的超时重试的机制,此时服务端处于LAST-ACK状态。
这种情况下,有两种情况:在超过一定时间后,服务器直接进行关闭,这种类似于客户端的关闭;另一种情况则是收到客户端的RST消息,关闭连接。
RST消息是一种重置消息,表示当前错误了,应该回到初始的状态。如果客户端跑路后有新的客户端接入,会在此发送SYN以期望建立连接,此时这个SYN将被忽略,并直接回复FIN+ACK消息,新客户端在收到FIN消息后是不会认的,并且会回复一个RST消息。