关闭连接的⽅式通常有两种,分别是
RST
报⽂关闭和
FIN
报⽂关闭。
如果进程异常退出了,内核就会发送
RST
报⽂来关闭,它可以不⾛四次挥⼿流程,是⼀个暴⼒关闭连接的⽅式。
安全关闭连接的⽅式必须通过四次挥⼿,它由进程调⽤
close
和
shutdown
函数发起
FIN
报⽂(
shutdown
参数须传⼊ SHUT_WR
或者
SHUT_RDWR
才会发送
FIN
)。
1
、
close
和
shutdown
有什么区别
调⽤了
close
函数意味着完全断开连接,完全断开不仅指⽆法接收数据,⽽且也不能发送数据。
此时,调⽤了
close
函数的⼀⽅的连接叫做「孤⼉连接」,如果你⽤
netstat -p
命令,会发现连接对应的进程名为空。
使⽤
close
函数关闭连接是不优雅的。于是,就出现了⼀种优雅关闭连接的
shutdown
函数,它可以控制只关闭 ⼀个⽅向的连接
![](https://i-blog.csdnimg.cn/direct/9649aecdbc46449984f5ddecfa43d17f.png)
其中第⼆个参数决定断开连接的⽅式,主要有三种:
(
1
)
SHUT_RD(0)
关闭连接的「读」这个⽅向
,如果接收缓冲区有已接收的数据,则将会被丢弃,并且后续再收到新的数据,会对数据进⾏ ACK
,然后悄悄地丢弃。也就是说,对端还是会接收到
ACK
,在这种情况下根本不知道数据已经被丢弃了。
(
2
)
SHUT_WR(1)
关闭连接的「写」这个⽅向
,这就是常被称为「半关闭」的连接。如果发送缓冲区还有未发送的数据,将被⽴即发送出去,并发送⼀个 FIN
报⽂给对端。
(
3
)
SHUT_RDWR(2)
相当于
SHUT_RD
和
SHUT_WR
操作各⼀次,关闭套接字的读和写两个⽅向。
2
、
FIN_WAIT1
状态的优化
如果
FIN_WAIT1
状态连接很多,我们就需要考虑降低
tcp_orphan_retries
的值。当重传次数超过
tcp_orphan_retries
时,连接就会直接关闭掉(即:新增的孤⼉连接将不再⾛四次挥⼿,⽽是直接发送
RST
复位报⽂强制关闭)。
注:
tcp_max_orphans
参数,定义了「孤⼉连接」的最⼤数量。
3
、
FIN_WAIT2
状态的优化
当主动⽅收到
ACK
报⽂后,会处于
FIN_WAIT2
状态,就表示主动⽅的发送通道已经关闭,接下来将等待对⽅发送FIN 报⽂,关闭对⽅的发送通道。
这时,如果连接是⽤
shutdown
函数关闭的,主动⽅连接可以⼀直处于
FIN_WAIT2
状态,因为它可能还可以发送 或接收数据。
但对于
close
函数关闭的孤⼉连接,由于⽆法再发送和接收数据,所以这个状态不可以持续太久,⽽tcp_fin_timeout 控制了这个状态下连接的持续时⻓,默认值是
60
秒(与
TIME_WAIT 状态持续的时间是相同的)。
它意味着对于孤⼉连接(调⽤
close
关闭的连接),如果在
60
秒后还没有收到
FIN
报⽂,连接就会直接关
闭。
4
、
TIME_WAIT
状态的优化
TIME_WAIT
的状态尤其重要,主要是两个原因
![](https://i-blog.csdnimg.cn/direct/869a7b47b0d348d28c14a427aeb1f75f.png)
(
1
)防⽌收到历史数据,从⽽导致数据错乱的问题
若
TIME_WAIT
等待时间过短,被延迟的数据包抵达后会发⽣什么呢?
![](https://i-blog.csdnimg.cn/direct/101aa53fd36d4c84a65a513ca37576c1.png)
TIME_WAIT
设计为
2MSL
,⾜以让两个⽅向上的数据包都被丢弃,使得原来连接的数据包在⽹络中都⾃然消失,再出现的数据包⼀定都是新建⽴连接所产⽣的。
注:
MSL
全称是
Maximum Segment Lifetime
,它定义了⼀个报⽂在⽹络中的最⻓⽣存时间(报⽂每经过⼀次路由器的转发,IP
头部的
TTL
字段就会减
1
,减到
0
时报⽂ 就被丢弃,这就限制了报⽂的最⻓存活时间)。
(
2
)为什么是
2 MSL
的时⻓呢?
这其实是相当于⾄少允许报⽂丢失⼀次。⽐如,若
ACK
在⼀个
MSL
内丢失,这样被 动⽅
᯿
发的
FIN
会在第
2
个 MSL 内到达,
TIME_WAIT
状态的连接可以应对。
在
Linux
系统中,
MSL
的值固定为
30
秒。
等待⾜够的时间以确保最后的
ACK
能让被动关闭⽅接收,从⽽帮助其正确关闭
(
3
)假设
TIME_WAIT
没有等待或等待的时间过短,断开连接会造成什么?
![](https://i-blog.csdnimg.cn/direct/6de7d6176563410d9a954addbb844622.png)
如上图红⾊框框客户端四次挥⼿的最后⼀个
ACK
报⽂如果在⽹络中被丢失了,此时如果客户端
TIMEWAIT
过短或 没有,则就直接进⼊了 CLOSE
状态了,那么服务端则会⼀直处在
LAST-ACK
状态。
当客户端发起建⽴连接的
SYN
请求报⽂后,服务端会发送
RST
报⽂给客户端,连接建⽴的过程就会被终⽌。
注:
Linux
提供了
tcp_max_tw_buckets
参数,当
TIME_WAIT
的连接数量超过该参数时,新关闭的连接就不再经历TIME_WAIT ⽽直接关闭。查看系统的
TIME_WAIT
的连接数量: