监控网络状态

警告
本文最后更新于 2023-10-30,文中内容可能已过时。

监控网络状态

netstat network statistics

Linux netstat 命令用于显示网络状态。

利用 netstat 指令可让你得知整个 Linux 系统的网络情况。

netstat命令很简单,也很直接,用于显示与 IP、TCP、UDP 和 ICMP 协议相关的统计数据,一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序,它能提供 TCP 连接,TCP 和 UDP 监听,进程内存管理的相关报告。

1
netstat [-acCeFghilMnNoprstuvVwx][-A<网络类型>][--ip]

参数

  • -a(all):显示所有选项,不使用-a选项的时候(即默认情况)不显示 LISTEN 相关

  • -t(tcp):仅显示 tcp 相关选项

  • -u(udp):仅显示 udp 相关选项

  • -n:拒绝显示别名,能显示数字的全部转化成数字。这样的好处是可以直接看到 IP 而不是域名,方便进行二次搜索

  • -l:仅列出有在 Listen (监听) 的服務状态

  • -p:显示建立相关链接的进程 ID 和程序名,注意,这个参数,需要 root 权限才能看到所有

  • -r:显示路由信息,路由表

  • -e:显示扩展信息,例如 uid 等

  • -s:按各个协议进行统计

  • -c:每隔一个固定时间,执行该netstat命令。

提示:LISTEN 和 LISTENING 的状态只有用-a或者-l才能看到

常用 -a -n -p

使用 root 权限运行netstat -anp,输出

……

从整体上看,netstat的输出结果可以分为两个部分:

一个是 Active Internet connections,称为有源 TCP 连接,即网络连接,我们用netstat命令看的主要也是这个部分,

  • Proto:协议,tcp6 指的是 IPV6 的 tcp 协议。

  • “Recv-Q"和"Send-Q"指的是接收队列和发送队列。这些数字一般都应该是 0。如果不是则表示软件包正在队列中堆积。这种情况只能在非常少的情况见到。

  • Local Address:Linux 系统本机地址,

  • Foreign Address:系统外部地址,其中0.0.0.0和 127.0.0.1 指的都是本地,这是 IPV4 的格式,:::指的也是本地,是 IPV6 的格式。

我们可以很明显地看到 22 端口的监听(SSH):

172.16.20.1,指的其实就是 Linux 的宿主机:Windows,也就是我的 xshell 的所在的电脑,

所以这里实际上就是从宿主机的 55750 端口到虚拟机的 22 端口的一个 ssh 网络连接。

如果有多条,说明 xshell 里开了多个 ssh 连接。

一旦用户退出,ssh 连接断开,这一条网络连接信息就会消失,有时候也会有TIME-WAIT的中间过渡状态(这是由 TCP 协议的机制决定的)。

另一个是 Active UNIX domain sockets,称为有源 Unix 域套接口 (和网络套接字一样,但是只能用于本机通信,也就是本机内部的通信性能可以提高一倍)。

  • Proto 显示连接使用的协议,

  • RefCnt 表示连接到本套接口上的进程号,

  • Types 显示套接口的类型,

  • State 显示套接口当前的状态,

  • Path 表示连接到套接口的其它进程使用的路径名。

State 的类型

  • LISTEN:侦听来自远方的 TCP 端口的连接请求

  • SYN-SENT:再发送连接请求后等待匹配的连接请求(如果有大量这样的状态包,检查是否中招了)

  • SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(如有大量此状态,估计被 flood 攻击了)

  • ESTABLISHED:代表一个打开的连接

  • FIN-WAIT-1:等待远程 TCP 连接中断请求,或先前的连接中断请求的确认

  • FIN-WAIT-2:从远程 TCP 等待连接中断请求

  • CLOSE-WAIT:等待从本地用户发来的连接中断请求

  • CLOSING:等待远程 TCP 对连接中断的确认

  • LAST-ACK:等待原来的发向远程 TCP 的连接中断请求的确认(不是什么好东西,此项出现,检查是否被攻击)

  • TIME-WAIT:等待足够的时间以确保远程 TCP 接收到连接中断请求的确认

  • CLOSED:没有任何连接状态


本地 IP 地址和远程 IP 地址都是0.0.0.0,这表示通信还没开始,IP 地址不确定,进程的状态也是 LISTEN

UDP 协议中套接字不绑定对方的地址和端口,因此这里显示 *:*

这部分《网络是怎样连接的》的《用电信号传输 TCP/IP 数据 —— 探索协议栈和网卡》的套接字的实体就是通信控制信息


常用:

netstat -i:显示网卡列表,跟ifconfig查看的结果一样

netstat -a:列出所有端口 (包括监听和未监听的)

netstat -at:列出所有 TCP 协议端口

netstat -au:列出所有 UDP 协议端口

netstat -l:只显示正在监听的端口

netstat -lt:只显示正在监听的 TCP 端口

netstat -lu:只显示正在监听的 UDP 端口

netstat -s:显示所有端口的统计信息

netstat -st:显示 TCP 端口的统计信息

netstat -su:显示 UDP 端口的统计信息

netstat -ap,列出所有端口,加上进程信息 -p

netstat -c:将每隔一秒输出网络信息。

经常是对netstat -anp的结果再进行二次的搜索 ,比如

netstat -anp | grep ssh

netstat -anp | grep ':80'

……

常见问题处理

大量的 TCP 连接的状态为 CLOSE_WAIT

背景知识

参考《网络是怎样连接的》的《用电信号传输 TCP/IP 数据 —— 探索协议栈和网卡》的数据收发操作小结小节

原因

Linux 服务器 tcp socket 常见的几种状态:ESTABLISHED、TIME_WAIT、CLOSE_WAIT。

TCP 协议中描述,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。

提供网络服务时,需要特别关注两种状态:CLOSE_WAIT 和 TIME_WAIT。如果过多,可能导致 socket 资源耗尽,无法正常提供服务。

  • ESTABLISHED:表示正在 socket 通信。

  • TIME_WAIT(第 3 步以后):表示请求的客户端主动关闭 socket 而形成的状态。等待 2MSL 时间,约 4 分钟。主要是防止最后一个 ACK(第 4 步)丢失。由于 TIME_WAIT 的时间会非常长,因此服务端应尽量减少主动关闭连接。之所以设计 TIME_WAIT 状态,是为了考虑 ACK(第 4 步)丢失的情况发生,服务端将重发 FIN(第 3 步),客户端必须维护 TCP 状态信息以便可以重发最终的 ACK(第 4 步),否则会发送 RST,结果服务端认为发生错误。

  • CLOSE_WAIT(第 1 步以后):表示服务端被动关闭 socket。根据 TCP 状态机,服务器端收到客户端发送的 FIN,则按照 TCP 实现发送 ACK,因此进入 CLOSE_WAIT 状态。但如果服务器端不执行 close(),就不能由 CLOSE_WAIT 迁移到 LAST_ACK,则系统中会存在很多 CLOSE_WAIT 状态的连接。

也就是说,TIME_WAIT、CLOSE_WAIT 不可能出现在一台主机的一个 TCP 连接中,他要么是主动关闭的一方,要么是被动关闭的一方

主动关闭的一方的状态变化过程是 主动关闭 -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED

被动关闭的一方的状态变化过程是 被动关闭 -> CLOSE_WAIT -> LAST_ACK -> CLOSED

Linux 服务器出现大量的 CLOSE_WAIT 后,socket 资源会被耗尽。因为 Linux 分配给一个用户的文件句柄是有限的,而 TIME_WAIT 和 CLOSE_WAIT 两种状态如果一直被保持,则文件句柄也就不能 close,导致句柄资源达到上线,接着就会出现大量 Too Many Open Files 错误。

如果出现大量 CLOSE_WAIT,首先需要考虑被动关闭方的程序是否有 BUG,是否有 socket 泄露(遗漏 close())。如果别的机器上能够正常访问,TCP 正常连接,那么就不是代码问题,那么就要关注网络问题,是不是因为主动关闭方的 ACK 号(第 3 步)因为网络问题无法发送到被关闭方。导致被关闭方一直 CLOSE_WAIT/LAST_ACK。

查看当前系统中的 tcp socket 状态信息并统计:

netstat -anp | grep TIME_WAIT  | wc -l

netstat -anp | grep CLOSE_WAIT | wc -l

处理方法

CLOSE_WAIT:把占用的线程kill掉即可,如果是 TIME_WAIT,过一段时间,就会自己释放。

0%