Timewait 是什么
我们只知道在四次挥手的过程中,先发起关闭的一方会进入 TIMEWAIT 状态,为什么会出现 TIMEWAIT 状态以及如果 TIME_WAIT 状态过多,是什么原因?
1 出现的场景
在 TCP 建立连接对外提供服务的过程中,每个链接会占用一个本地端口,如在高并发的情况下,TIME_WAIT 状态过多,势必会占用大量的端口,端口又有限,以致于耗尽端口,所以会出现偶尔链接的上,偶尔断开的情况
这么多的 TIME_WAIT 哪里来的呢?先复习下四次挥手
![](https://static001.geekbang.org/infoq/6c/6c40ebf9281eb92d8e96d6c08fd368fd.png)
[FINWAIT1] :FINWAIT1 和 FINWAIT2 均为等待对方的 FIN 报文。两者区别为,当 SOCKET 在 ESTABLISHED 状态时,想主动关闭连接从而想对方发送 FIN 报文,此时进入 FINWAIT1 状态。当收到 ACK 报文进入 FIN_WAIT2 状态。
[FINWAIT2]:此状态下的 socket 实际上表示半连接的状态。什么是半连接,是一方想要关闭连接,另一方说稍等下,我还有点数据给你哈
[FIN_WAIT] :表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 后即可回到 CLOSED 可用状态了
[CLOSEWAIT] : 反过来读比较好,WAITCLOSE 即「等待关闭」。在被动关闭连接情况下,在已经接收到 FIN,但是还没有发送自己的 FIN 的时刻,连接处于 CLOSE_WAIT 状态。
[LAST_ACK] :被动关闭乙方发送 FIN 后,等待对方的 ACK,收到 ACK 即进入 CLOSED 状态
在 TCP/IP 协议栈中,假设主机 A 想要进行链接终止操作,于是发送 FIN 报文给主机 B,主机 B 收到从而进入 CLOSEWAIT 状态并发送 ACK 作为应答,同时主机 B 会告诉应用程序也要关闭操作,于是发送 FIN 报文。主机 A 收到 FIN 报文发送 ACK 表示收到并进入 TIMEWAIT
在 Linux 系统中有一个字段,名称为 TCPTIMEWAIT_LEN,其数值为 60s,也就是需要在 TIME_WAIT 阶段停留 60s
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ZqAgWv8-1595251734360)(https://static01.imgkr.com/temp/d4cccc089a7243d5b70c17b187f40fcc.png)]
2 TIME_WAIT 什么作用
从上图我们知道 TIME_WAIT 过后还有 CLOSE 状态,为什么不直接进入 CLOSED 状态,而是要过一会呢?
第一点,在 TIME_WAIT 后,为了确保 ACK 能让被动方接收并辅助其关闭。假设这样的情况
如果主机 A 的 ACK 报文传输失败,那么在 TCP/IP 协议栈设计中,主机 B 会重传 FIN 报文,但是此时主机 A 没有维护好 TIME_WAIT 状态而是 CLOSED 状态,此时主机 A 只好发送 RST,从而被动关闭失败。
第二点,为了让就连接的重复节点在网络中自然消失。怎么理解?
我们知道,在网络传输的过程中,总会因为各种故障导致报文不能准时到达目的地。现在我们假设使用[源 IP,源端口,目的 IP,目的端口]代表连接 A,在通信的过程中,链接 A 因为某种原因中断,暂时迷路,从而创建了类似于 A 的连接 B,可是迷路的连接 A 到达了,势必就会对 TCP 通信产生影响。所以设置了 2MSL,足够的时间让两个方向的分组都消失,使得下一个新的连接不会出现旧的连接请求报文
3 TIME_WAIT 哪些危害
内存资源占用
端口占用。端口资源有限,一般可开启端口为 32768~61000,可以通过修改 net.ipv4.iplocalport_range 指定,如果 TIME_WAIT 太多将无法建立连接
你可以通过下面命令查看当前 TIME_WAIT 数量
```
netstat -nt | awk '/^tcp/ {++state[$NF]} END {for(key in state) print =key,"t",state[key]}' TIME_WAIT 28233
```
这里为什么是 28233 呢,取决于内核参数 net.ipv4.iplocalrange
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jj7cb12b-1595251734365)(https://static01.imgkr.com/temp/c47dab033bdc487aab40d3e23ba6f5f0.png)]
因为端口范围是一个闭区间,所以实际可用的端口数量是:
4 TIME_WAIT 如何优化
暴力执法------net.ipv4.tcpmaxtw_buckets
一旦 TIME_WAIT 状态过多就重置,方法是使用 sysctl 将系统值减小,太粗鲁不推荐
调低 TCPTIMEWAITLEN
了解内核编译即可操作
SO_LINGER 设置
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen);
调整 tcptwrecycle
我们先看看 RFC1323 中怎么描述的
An additional mechanism could be added to the TCP, a per-hostcache of the last timestamp received from any connection.This value could then be used in the [PAWS] mechanism to rejectold duplicate segments from earlier incarnations of theconnection, if the timestamp clock can be guaranteed to haveticked at least once since the old connection was open. Thiswould require that the TIME-WAIT delay plus the RTT togethermust be at least one tick of the sender’s timestamp clock.Such an extension is not part of the proposal of this RFC.
net.ipv4.tcptwreuse 复用连接
使用这个选项有个前提,需要打开 TCP 时间戳的支持 net.ipv4.tcp_time stamps=1,在 RFC1323 中,为了保证 TCP 的高可用,引入了两个 4 字节的时间戳选项,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。这就有意思了,之前说的 2MSL 就不存在了,因为如果重复的数据包会因为时间戳的过期而被丢弃
只适用于连接发起方(C/S 模型中的客户端),这里为什么强调是客户端,我们看看源码;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FTWbVgsR-1595251734390)(https://static01.imgkr.com/temp/a3277a3290f94ac4ae11a4d1f0d05020.png)]
其调用路径仅仅出现在 tcpv4connect->inethashconnect->_inetcheckestablished->twskunique->twskunique。 也就是说 tcptw_reuse 仅在 TCP 套接字作为客户端,调用 connect 时起作用。作为服务端很少主动发起链接,所以对于服务端而言不用打开此选项
对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用。
最后引用一下 W. Richard Stevens 在《UNIX 网络编程》的一句话
The TIME_WAIT state is our friend and is there to help us (i.e., to let old duplicate segments expire in the network). Instead of trying to avoid the state, we should understand it.
译者:存在即合理,勇敢面对,而不是逃避。
巨人的肩膀
《Coping with the TCP TIME-WAIT state on busy Linux servers》
Tuning TCP and nginx on ec2 -p30
TIME_WAIT and its design implications for protocols and scalable client server systems
tcptwrecycle 和 tcp_timestamps 导致 connect 失败问题
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
tcptwrecycle 参数引发的系统问题 - http://blog.csdn.net/zhuyiquan/article/details/68925707
唠嗑
有收获?麻烦老铁们来个三连击,这样更多的人看到这篇文章
1、给小弟点个赞呗,这样我也更有动力肝文,嘻嘻。
2、老铁们,关注我的原创微信公众号「我是程序员小贱」,专注于高频面试题的题解,其中也已经写了十多篇大厂面经。希望一起进步!
_
版权声明: 本文为 InfoQ 作者【我是程序员小贱】的原创文章。
原文链接:【http://xie.infoq.cn/article/efa65784e51d24d0c8daa156b】。文章转载请联系作者。
评论