TCP四次挥手
所谓四次挥手(Four-Way Wavehand
)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。
连接双方在完成数据传输之后就需要断开连接。由于TCP连接是属于全双工的,即连接双方可以在一条TCP连接上互相传输数据,因此在断开时存在一个半关闭状态,即有有一方失去发送数据的能力,却还能接收数据。因此,断开连接需要分为四次
流程如下:
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
第一次挥手:Client将标志位FIN和ACK置为1并且发送发送一个FIN和ACK,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号Ack为收到Seq+1(与SYN相同,一个FIN占用一个序号),序号Seq=1,Server进入CLOSE_WAIT状态。
第三次挥手:Server发送一个FIN,标志位FIN和ACK置为1,用来关闭Server到Client的数据传送,Seq=y,Ack=上次的Seq+1,Server进入LAST_ACK状态。
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号Ack为收到序号Seq+1,Server进入CLOSED状态,完成四次挥手。
4.为什么建立连接需要三次握手?
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接。确保数据能够完整传输。
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发
送。
5.为什么断开连接需要四次挥手?
当被动方收到主动方的FIN报文通知时,它仅仅表示主动方没有数据再发送给被动方了。
但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET,它可能还需要发送一些数据给主动方后,再发送FIN报文给主动方,告诉主动方同意关闭连接,所以这里的ACK报文和FIN报文多数情况下都是分开发送的。
6.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND
状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK
状态下的SOCKET
可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT
状态的作用就是用来重发可能丢失的ACK报文。
7.SYN攻击原理
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect
),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。
SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以可以查看SYN_RECV
状态:
# netstat -nap | grep SYN_RECV
关于建连接时SYN超时:试想一下,如果server端接到了clien发的SYN后回了SYN-ACK
后client掉线了,server端没有收到client回来的ACK,那么,这个连接处于一个中间状态,即没成功,也没失败。于是,server端如果在一定时间内没有收到的TCP会重发SYN-ACK
。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻售,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才会把断开这个连接。
关于SYN Flood攻击:一些恶意的人就为此制造了SYN Flood攻击——给服务器发了一个SYN后,就下线了,于是服务器需要默认等63s才会断开连接,这样,攻击者就可以把服务器的syn连接的队列耗尽,让正常的连接请求不能处理。于是,Linux下给了一个叫tcp_syncookies
的参数来应对这个事——当SYN队列满了后,TCP会通过源地址端口、目标地址端口和时间戳打造出一个特别的Sequence Number
发回去(又叫cookie),如果是攻击者则不会有响应,如果是正常连接,则会把这个 SYN Cookie
发回来,然后服务端可以通过cookie建连接(即使你不在SYN队列中)。请注意,请先千万别用tcp_syncookies
来处理正常的大负载的连接的情况。因为,synccookies
是妥协版的TCP协议,并不严谨。对于正常的请求,你应该调整三个TCP参数可供你选择,第一个是:tcp_synack_retries
可以用他来减少重试次数;第二个是:tcp_max_syn_backlog
,可以增大SYN连接数;第三个是:tcp_abort_on_overflow
处理不过来干脆就直接拒绝连接了。