说明连接过程和释放过程时,请先熟悉以下名称。
- SYN
全称为 Synchronize Sequence Numbers,同步序列编号,实际上是客户端首次建立连接时会先随机生成一个编号,请求会携带该编号与服务端建立连接。
- ACK
确认序号有效
- FIN
断开连接所使用
- seq(SequenceNumber)
序号
- ack(AcknowledgeNumber)
确认序号
三次握手

过程
-
客户端将
SYN标志位设置为 1,生成随机数字x,然后将携带seq=x的请求发送给服务端,发送完毕之后客户端进入SYN-Send状态,等待服务器的确认。 -
服务端收到客户端发送的请求后,想客户端发送一个
SYN=1、ACK=1的请求,同时该请求会携带seq=y、ack=x+1,表示希望接收到客户端seq=x+1的请求,发送完毕之后,服务端进入SYN-Received状态。 -
客户端收到服务端的
SYN-ACK答复后,确认x+1无误后,再次发送ack=y+1、seq=x+1给服务端,该链接发送完毕之后,双方进入Established状态。
一些疑问
-
为什么是三次握手而不是两次、一次
因为网络的不确定性,无法保证先发送的请求经过多长时间才会到达,一旦超过了
RTO(Retransmission TimeOut)而导致客户端重发SYN报文,此时若没有第三次连接会导致客户端创建过多的连接而导致资源浪费,第三次握手的过程将会导致之前的报文被拒绝。
-
SYN 洪泛攻击是什么?如何解决这个问题?
服务端收到
SYN报文准备返回SYN+ACK报文时,将会分配并初始化连接变量和缓存,这个时候其实相当于半开连接 (half-open coneection),会消耗一定的服务器资源,如果客户端此时返回ACK报文,那么双方就会正常建立连接,否则服务器将会在等待一分钟后关闭该连接回收资源,这样的机制给某些攻击者以可乘之机,攻击者将会使用客户端发送大量的SYN报文进而服务端开启大量的半开连接,另外客户端没有给予服务器ACK应答时会导致服务器重发SYN+ACK报文,这两种情况过多会导致服务器无法继续为其他连接提供服务,这种方式成为 SYN 洪泛攻击,这也是 DDos(拒绝服务攻击)攻击的一种。
解决方法:
SYN Cookie、网关过滤、缩短SYN-TimeOut超时时间SYN Cookie是对三次握手过程的一些修改,在服务器收到SYN报文并且返回SYN+ACK报文时,不再开启一个半开连接,也不初始化资源,而是根据SYN报文段的部分重要信息,使用Hash函数计算出一个特定值作为cookie,这个cookie将作为SYN+ACK报文中的序列号ISN(seq),当客户端返回一个ACK报文段时,服务端重新使用Hash函数进行计算,然后与返回的序列号进行对比,如果相同则开始分配资源,如果不相同则拒绝连接。Linux 服务器可以使用一下命令来查看半开连接数量。
netstat -n -p TCP | grep SYN_RECV
-
三次握手的过程中那些可以携带用户数据?
第一次和第二次不能携带数据,因为这是尚且不知道双方的连接情况,此时如果可以携带数据的话将会给攻击者可乘之机,只要在
SYN中携带大量数据,服务器很可能因为接受请求而消耗过多的资源或内存。第三次握手是可以携带数据的,因为此客户端已经成为
Established状态,并且已经知道服务端状态正常。
四次挥手
过程
-
客户端发送一个
FIN报文,报文中指定序列号x,发送完毕后进入FIN_WAIT1状态,其实也就是客户端停止发送数据,主动关闭TCP连接,进入终止等待1状态。 -
服务端收到客户端的
FIN报文后,向客户端发送ACK报文,x + 1作为ack,y作为seq,表明服务端收到了客户端的FIN报文,发送完毕之后服务端进入CLOSE_WAIT状态,客户端收到该请求后将进入FIN_WAIT2状态,等待服务端发出的连接释放报文。 -
服务端如果想断开连接,同样会向客户端发送一个
FIN + ACK报文,报文中指定序列号z,发送完毕后服务端处于LAST_ACK状态。 -
客户端收到服务端的
FIN + ACK报文后,发出ACK报文作为应答,此时客户端处于TIME_WAIT状态,需要过段时间确保服务端收到自己的ACK报文之后才会进入CLOSE状态,服务端在收到客户端的ACK报文后也会进入CLOSE状态。

一些疑问
-
为什么等待2MSL才会到closed状态
MSL全称是Maximum Segment Lifetime,其实也就是任何报文在网络上最长的等待时间,超过改时间的报文将会被丢弃,这里等待的意义是在客户端发送
ACK请求完毕后等待该段时间来确定服务器已经收到了该请求,如果服务端没有收到该请求,超时后服务端将重发FIN + ACK请求,如果没有等待时间,一旦因为网络问题服务端没有接收到客户端的ACK请求,此时无等待时间客户端发送完毕之后立即关闭连接,而服务端状态不可知,重试会因为服务端已经关闭连接而失败,这将导致服务端的关闭连接异常。