封面图片

编程

网络编程指南:什么是TCP连接状态

工作中我们经常会遇到网络连接问题,这时候我们会去服务器上,使用netstat等命令查看端口或者进程的连接状态。通过连接状态来帮助判断到底是什么地方出了问题。 常见的连接状态有LISTEN、ESTABLISHED、TIME_WAIT和CLOSE_WAIT。


  • LISTEN表示正在监听
  • ESTABLISHED表示已经建立好了连接
  • TIME_WAIT表示进程主动关闭了连接
  • CLOSE_WAIT表示进程被动关闭了连接

举个例子看一下:

1> netstat -nat | grep -i 12800 2tcp6 0 0 :::12800 :::* LISTEN 3tcp6 0 0 192.168.150.233:12800 192.168.14.8:57662 ESTABLISHED 4tcp6 0 0 192.168.150.233:12800 192.168.14.8:57350 TIME_WAIT

12800端口是一个java进程端口,上面有2个来自14.8客户端的请求,一个是ESTABLISHED状态,一个是TIME_WAIT状态。 这2个连接都是通过postman向java发送的请求。postman在发送请求时,设置了请求头connection:keep-alive。所以这里会是一个长连接,即ESTABLISHED状态。当超过一定时间不请求时,连接状态就会变为TIME_WAIT,Java进程主动关闭了这个连接。

CLOSE_WAIT

什么情况下会出现CLOSE_WAIT

当客户端发送了断开连接的请求后,服务端没有及时响应,导致服务端出现CLOSE_WAIT。一个常见的场景是客户端发送了一个请求,服务端处理这个请求比较耗时,可能5分钟才能处理完这个请求,而客户端等不及了,在发送请求1分钟后因为没有收到返回,就发送了中断连接的命令,而服务端忙于处理该请求内容,没有及时响应这个中断命令,导致CLOSE_WAIT的出现。 当出现CLOSE_WAIT的情况下,一定是代码出了问题,去检查软件的代码是否存在bug或者耗时的功能。

复现CLOSE_WAIT

使用postman向后端接口发送请求。设置postman的request timeout 为10000ms,请求超时10s。 后端打断点,阻止向postman返回请求数据。postman再超过10秒后主动释放了连接。而后端请求仍然是运行状态。 刚发送请求时状态:

1> netstat -nat | grep -i 29009 2tcp6 0 0 ::1.29009 ::1.52430 ESTABLISHED 3tcp6 0 0 ::1.52430 ::1.29009 ESTABLISHED

10秒之后的状态:

1> netstat -nat | grep -i 29009 2tcp6 0 0 ::1.29009 ::1.52430 CLOSE_WAIT 3tcp6 0 0 ::1.52430 ::1.29009 FIN_WAIT_2

端口为29009的进程作为server出现CLOSE_WAIT。 端口为52430的POSTMAN作为client主动发送断开连接的请求,所以状态是FIN_WAIT_2。

当大量出现CLOSE_WAIT时该怎么做

去检查代码,优化代码。

TIME_WAIT

什么情况下会出现TIME_WAIT

当服务端服务完成了请求任务,主动关闭了连接。这个时候连接会显示TIME_WAIT。之后连接会根据系统配置是选择复用,还是直接回收。

当大量出现TIME_WAIT时该怎么做

TIME_WAIT出现之后,经过一定的时间会被系统回收,这个时间是2MSL(max segment lifetime)时间。这个是TCP协议的规定。 操作系统默认TIME_WAIT的TCP连接回收时间是2分钟。在高并发场景下,这样会导致回收TCP过慢导致系统吞吐量下降。

通过修改Linux内核参数来调整
1> vi /etc/sysctl.conf 2## 添加如下内容 3net.ipv4.tcp_syncookies = 1 4net.ipv4.tcp_tw_reuse = 1 5net.ipv4.tcp_tw_recycle = 1 6net.ipv4.tcp_fin_timeout = 30 7 8> /sbin/sysctl -p
  • net.ipv4.tcp_syncookies = 1  表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
  • net.ipv4.tcp_tw_reuse = 1  表示开启重用。允许将TIME_WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
  • net.ipv4.tcp_tw_recycle = 1  表示开启TCP连接中TIME_WAIT sockets的快速回收,默认为0,表示关闭。
  • net.ipv4.tcp_fin_timeout  修改系統默认的TIMEOUT时间
使用长连接

如果是后端接口,可以使用nginx来反向代理,并设置为长连接。

常用命令

1# 查看建立连接的数量,同理可以查看close_wait 和time_wait的连接 2netstat -nat | grep -i ESTABLISHED | wc -l 3 4# 查看端口的连接占用情况 5netstat -nat | grep -i 3306 6 7# 查看连接数 8netstat -nat | wc -l 9 10# 查看端口可用范围 11sysctl -a | grep port_range

相关问题

Can't assign requested address

连接服务器或服务器上相关服务时报以上错误。 产生这个错误的原因是由于 Linux 分配的客户端连接端口用尽,无法建立 socket 连接导致的。 那么在大量频繁建立连接时,而端口又不是立即释放,默认是 60s,就会出现客户端端口不够用的情况。 这就是这个问题的本质。 那么使用上面常用命令的查看连接数和端口可用范围即可判断是否存在这个问题。

解决

怎么解决呢?有两个方案:

  1. 调低 TIME_WAIT 时间
  2. 调高可用端口范围
调低 TIME_WAIT 时间

编辑内核文件 /etc/sysctl.conf,增加以下内容:

1// 表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理, 2// 可防范少量 SYN 攻击,默认为 0,表示关闭; 3net.ipv4.tcp_syncookies = 1 4// 表示开启重用。允许将 TIME-WAIT sockets 重新用于新的 TCP 连接,默认为 0,表示关闭; 5net.ipv4.tcp_tw_reuse = 1 6// 表示开启 TCP 连接中 TIME-WAIT sockets 的快速回收,默认为 0,表示关闭。 7net.ipv4.tcp_tw_recycle = 1 8// 修改系默认的 TIMEOUT 时间,默认为 60s 9net.ipv4.tcp_fin_timeout = 30
调高可用端口范围

编辑内核文件 /etc/sysctl.conf,增加以下内容:

1// 表示用于向外连接的端口范围。设置为 30000 到 65535。 2net.ipv4.ip_local_port_range = 30000 65535

最后,执行 sysctl -p 使参数生效。

2023年08月22日
在初学者眼中,世界充满了可能;专家眼中,世界大都已经既定。--铃木俊隆