一、三次握手与四次挥手
时序图
状态转移图
1.1 建立连接的目的:
分配资源、初始化序列号(通知peer对端我的初始序列号是多少)
也就是说,连接的双方都需要告诉对方自身的网络传输序列号;所以在建立连接的时候,服务端也需要将自己的SYN号发送给对方。
1.2 握手的交互过程
- clien 端首先发送一个 SYN 包告诉 Server 端我的初始序列号是 X;
- Server 端收到 SYN 包后回复给 client 一个 ACK 确认包,告诉 client 说我收到了;
- 接着 Server 端也需要告诉 client 端自己的初始序列号,于是 Server 也发送一个 SYN 包告诉 client 我的初始序列号是Y;
- Client 收到后,回复 Server 一个 ACK 确认包说我知道了。
对于Server端的返回数据有两种,一种是ACK包,一种是自己的SYN包,这两种包没有必要分两次发送,因此,TCP中就将两种包合在了一起。最终变成了三次握手。但是有时候是可能出现四次握手的。
如果Client发完一次SYN包就挂掉了,这个时候就没有成功建立连接;在Linux上,默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了.所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接。
由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称 SYN flood 攻击),用于耗尽Server的SYN队列。对于应对SYN 过多的问题,linux提供了几个TCP参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。
1.3 四次挥手的目的
回收资源、终止数据传输。由于TCP是全双工的,需要Peer两端分别各自拆除自己通向Peer对端的方向的通信信道。
1.4 四次挥手的连接拆除过程
- Client 发送一个FIN包来告诉 Server 我已经没数据需要发给 Server了;
- Server 收到后回复一个 ACK 确认包说我知道了
- Server 在自己也没数据发送给client后,Server 也发送一个 FIN 包给 Client 告诉 Client 我也已经没数据发给client 了;
- Client 收到后,就会回复一个 ACK 确认包说我知道了。
由于Server端只有再自己没有数据要发给Client端之后才能拆除自己到Client的通道,因此,它就是四次挥手,而不是三次挥手。但是,如果此时Server已经没有数据需要传给client了,就可以将会去的FIN和ACK合成一个数据包了,此时就是三次挥手(在某些系统的实现就是这样的)
二、TIME_WAIT 状态
2.1 TIME_WAIT的功能
四次挥手可以归纳为三个过程:
- 过程一:主动关闭方发送FIN;
- 过程二:被动关闭方收到主动关闭方的FIN后发送该FIN的ACK,被动关闭方发送FIN;
- 过程三:主动关闭方收到被动关闭方的FIN后发送该FIN的ACK,被动关闭方等待自己FIN的ACK。
问题就在过程三中,据TCP协议规范,不对ACK进行ACK,如果主动关闭方不进入TIME_WAIT,那么主动关闭方在发送完ACK就走了的话,如果最后发送的ACK在路由过程中丢掉了,最后没能到被动关闭方,这个时候被动关闭方没收到自己FIN的ACK就不能关闭连接,接着被动关闭方会超时重发FIN包,但是这个时候已经没有对端会给该FIN回ACK,被动关闭方就无法正常关闭连接了,所以主动关闭方需要进入TIME_WAIT以便能够重发丢掉的被动关闭方FIN的ACK。
2.2 TIME_WAIT的问题
- 作为服务器,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,占据大量的tuple,严重消耗着服务器的资源;
- 作为客户端,短时间内大量的短连接,会大量消耗的Client机器的端口,毕竟端口只有65535个,端口被耗尽了,后续就无法在发起新的连接了。
由于上面两个问题,作为客户端需要连本机的一个服务的时候,首选UNIX域套接字而不是TCP 。