服务器网络连接数排查:从ss/netstat到TCP状态分析

服务器网络连接数排查:从ss/netstat到TCP状态分析 没遇到过服务器网络连接数异常的人很难体会那种明明CPU、内存、磁盘都正常但服务却卡得要死的感觉是啥样的。我在一次处理线上接口超时问题时折腾了一圈才发现是服务器上的TIME_WAIT连接堆积到十几万把端口资源吃光了业务请求根本建立不了新连接。自那以后我但凡排查性能问题第一件事就是连上服务器看一眼网络连接数——这个指标看着基础但它往往能最直接地反映出连接池耗尽、流量异常、被扫描攻击、负载均衡转发异常等问题。这篇文章会围绕“在服务器上查看网络连接数”展开覆盖Linux和Windows两套主流环境下的常用命令、统计状态、聚合分析方法以及我是怎么利用这些数据做综合定位的。适合系统运维、后端开发、SRE、刚入门的小白也包括那些和我一样“遇到问题才想起来看网络”的朋友。1. 先搞清楚服务器上的连接是怎么暴露问题的1.1 网络连接数不是“一个数字”而是一堆有用状态很多刚做运维的朋友上来就执行netstat -an | wc -l然后把结果当成“连接数”来报——这个数字只能说明当前有多少条连接条目但实际价值有限。真正的分析要看连接的五元组、状态、所属进程和端口分布只有把这些维度拆开了才能判断服务器目前是否健康。一条TCP连接从建立到关闭会经历SYN_SENT、ESTABLISHED、FIN_WAIT_1、CLOSE_WAIT、TIME_WAIT等多个状态。在服务器上执行一个简单的ss -s就能看到当前所有TCP socket状态的汇总统计。我在日常巡检里习惯把这个输出作为“第一眼指标”如果ESTABLISHED状态数骤降可能是上游流量没进来如果TIME_WAIT数居高不下说明短连接场景下连接没有及时回收如果SYN_RECV堆积那基本可以怀疑是半连接攻击或者后端服务响应过慢。真正理解这些状态代表什么遇到连接数异常时就不会慌。举一个典型例子Tomcat部署的应用报“Connection reset”或者“Broken pipe”这时候你去服务器上看网络连接十有八九能看到大量CLOSE_WAIT这通常说明应用代码里没有正确关闭Socket连接底层连接已经断开应用自己还不知道还在占着文件描述符。单纯加连接数是解决不了这类问题的必须先定位到CLOSE_WAIT集中在哪个进程再去查对应代码逻辑。1.2 查看连接数的常用命令选型Linux服务器上现在主流的两条命令是netstat和ss。我个人的习惯是优先用ssss直接读取内核的socket的信息不依赖/proc/net/tcp解析在高连接数场景下速度要快一个量级而且输出信息更全。你只要在服务器上体验一次先跑netstat -an | wc -l再跑ss -s对比一下响应时间应该就能感受到差距。不过netstat也不是完全没用了很多老服务器、旧脚本还有依赖而且有些最小化安装的Linux发行版默认只带netstat装ss还得自己搞一下iproute2。Windows环境下我一般用netstat -ano这里的-o能显示PID方便我顺着进程号去任务管理器里手撕对应的应用。Windows的命令提示符下没有ss但资源监视器里自带“网络”标签页可以看到TCP连接列表和按进程统计的连接数图形界面在那个环境下反而更适合一眼扫出异常。后期系统比如Windows Server 2019以上还能配合PowerShell里的Get-NetTCPConnection来查询但它的输出格式我个人觉得不如 netstat 直观日常反而用得少。2. 动手实操用这些命令查看网络连接数2.1 Linux环境下查看TCP连接状态汇总要快速了解一台服务器的整体网络连接概貌我通常先用ss -s。这条命令会输出一段类似这样的文字TCP协议统计会显示“estab”、总连接数、time wait、close wait等字段。不需要额外参数执行速度极快即使机器上已经建立了上百万条连接也能秒级返回光这一点就已经取决于现场效率了。如果需要看得更细那就用ss -ant这条命令把当前所有TCP连接按状态、本端地址、对端地址、队列等字段全列出来。这里注意如果服务器是生产环境且连接数上了万直接执行ss -ant会把终端刷得没完没了我一般先| head -n 20看个开头或者配合后面的统计命令直接做聚合。真正的分析手段不是看每条连接记录而是看统计结果比如我按状态分个组ss -ant | awk NR1 {print $1} | sort | uniq -c | sort -rn执行后能得到一个各状态分布数量的表比如15 LISTEN、3200 ESTAB、8000 TIME-WAIT这样。这一招在我排查问题时经常是第一步上来就知道当前连接基本都是哪种状态后续的排查方向也就清晰了。比如看到SYN-RECV数量很高我第一反应就是去看是不是有外部IP在扫描端口或者被人打了SYN Flood。检查监听端口的情况我习惯用ss -lntp。-p参数会把对应进程的PID和进程名显式打印出来比如users:((nginx,pid1532,fd12))调试的时候就能直接看到端口被谁占着是不是预期内的业务进程还是有人在你机器上跑了个奇怪的服务。这个命令等效于netstat -lntp但响应速度更让人痛快。2.2 Windows环境下如何查看和筛选连接Windows服务器没有ss但netstat足够解决日常问题。我通常在cmd或者PowerShell里执行netstat -ano输出里最后一列是PID第二列是本机IP加端口第三列是远端地址第四列是状态。要看某个端口被谁占用比如排查IIS绑定的80端口直接配合findstr过滤netstat -ano | findstr :80这样能把所有涉及80端口的连接都打出来再根据PID到任务管理器里确认是哪个进程。如果还想确认某个PID名下具体有哪些连接可以再过滤一次PID列。多个筛选条件结合排查进程的网络行为就基本足够了。如果是Windows Server 2012以上的系统我也偶尔用PowerShellGet-NetTCPConnection -State Established | Group-Object State | Select-Object Name, Count这种方式的筛选和分组能力比 netstat 更灵活但个人不是一个重度PowerShell用户平时不会动不动就去写脚本。对于临时看几眼我还是觉得 netstat 够用。如果要在Windows上做复杂的连接统计比如按远端IP聚合连接数我反而会导出数据后再用Excel或者脚本做二次分析而不是在命令行里死磕。2.3 统计脚本把连接数变成可分析的表格查看连接数这一步很简单但要在生产服务器上快速判断“连接数高了是不是有鬼”光靠肉眼看几百上千行输出不现实。我的做法是用一条管道命令把连接数据聚合起来这里分享几个高频使用的统计命令。按客户端IP统计连接数排名前20ss -ant | awk {print $5} | awk -F: NF2 {print $1} | sort | uniq -c | sort -rn | head -n 20这条命令的结果会告诉我当前哪些IP正在大量连接服务器。比如以前有一个场景我发现某一个内网IP的连接数占掉了一半以上查下去发现是测试环境的一个压测脚本没关一直在连生产库。没有这条命令我估计要翻半天连接列表才能定位到。按状态目标端口组合统计也很实用ss -ant | awk NR1 {print $1, $4} | awk -F {port$2; sub(/.*:/, , port); print $1, port} | sort | uniq -c | sort -rn | head -n 30这样能直接看到每个端口的连接状态分布比如80端口上基本都是ESTAB3306端口上有大量TIME_WAIT说明前端应用在频繁连数据库且是短连接可以考虑改成连接池。如果发现443端口上同时有大量SYN_RECV就要警惕是不是有人直接对HTTPS端口发起攻击了。统计脚本我一般只放在服务器上的一个固定目录比如/usr/local/bin/conn_stat.sh避免每次敲一长串管道符容易敲错也不方便记忆。3. 连接数的综合分析从数值现象到问题定位3.1 结合连接状态判断业务健康状况拿到连接数统计结果后第二轮问题就是“这些数字代表什么”我是从以下几个维度来判断的。如果ESTABLISHED连接数持续稳定在一个范围比如Web服务网关维持在几百上千说明后端服务能正常接受转发如果这个数字突然跌到几乎为0但负载均衡又说流量没有下降那我就会怀疑是不是后端应用挂了、端口没监听或是防火墙把连接拦了。TIME_WAIT连接数量大常见于高并发短连接的场景。TIME_WAIT本身是TCP正常关闭的一方进入的状态用于保证延迟的数据包不会干扰新连接。不过大量TIME_WAIT会占用内存和本地端口一旦本地端口耗尽新连接就无法发出我遇到过最极端的情况是TIME_WAIT占满了几万个端口导致业务几十秒不可用。处理此类问题的常用思路是开启net.ipv4.tcp_tw_reuse但只在客户端场景下管用如果服务器本身就是服务端TIME_WAIT主要出现在主动断开的一方以及调整net.ipv4.ip_local_port_range扩大可用端口范围。CLOSE_WAIT数量长时间高于个位数需要特别注意。CLOSE_WAIT意味着对端已关闭连接但本端进程没有完成关闭流程说白了就是程序里的连接没有正确close。如果这种状态不断累积文件描述符很快会耗尽应用会出现“Too many open files”报错。我处理过一起Java应用CLOSE_WAIT持续增长的问题最后定位到是HTTP连接池没设置空闲超时导致大量连接在服务端已被关闭但客户端还在持有。SYN_RECV状态数量异常偏高说明TCP三次握手只完成了两步回包发不出去可能原因包括后端服务accept队列满了、防火墙丢弃了SYN_ACK包、或者遭遇SYN泛洪攻击。这种情况我会同时看CPU和网络软中断负载如果软中断飙高多半是在应付大量无用的握手包。3.2 百万并发场景下的文件描述符与端口瓶颈连接数不是一个孤立指标它一定会反映到系统资源上。连接数一多最先受冲击的就是进程文件描述符数量、内核端口范围和内存分配。所以查看连接数不是终点还要再看这三个地方文件描述符限制方面在Linux上执行ulimit -n通常默认1024可能不够生产环境用我会评估业务场景把nofile调高到65535甚至100万。很多故障现场就是文件描述符被打满新连接根本无法accept看起来表现就是“连接被拒绝”或“无法分配内存”。检查时可对比当前进程的fd占用ls -l /proc/PID/fd | wc -l端口范围方面客户端发起的连接需要占用本地端口可用的本地端口数量决定了一个IP对同一目标IP的最大并发连接数。查看范围cat /proc/sys/net/ipv4/ip_local_port_range我见过默认设置为32768~60999也就是总共约28000个端口如果短连接速率极高这个范围很容易被打满报错是“Cannot assign requested address”。日常调优时我会把起始端口调到1024左右扩大可用区间但前提是要避免与常用服务端口冲突。连接追踪表方面iptables/nftables防火墙状态下Linux会维护conntrack表项cat /proc/sys/net/netfilter/nf_conntrack_max cat /proc/sys/net/netfilter/nf_conntrack_count如果count接近max新连接会丢包或拒绝现象是网络时通时不通。检查和监控这两个数字能提前避免防火墙连接表爆满导致的隐性故障。3.3 判断服务器是被扫描还是被攻击的经验服务器放在公网上每天都会被扫描和试探这是常态。判断连接数是“正常业务流量”还是“被攻击”我会看这几个特征。大量不同源IP都连到同一个未被业务使用的端口比如22端口或者1433、3306等数据库端口但业务根本用不到这就是扫描。看统计ss -ant | awk NR1 {print $4} | awk -F: {print $NF} | sort | uniq -c | sort -rn如果某个不常用端口出现密集的SYN_RECV或大量短连接基本可以判定是扫描行为处理方式是防火墙封禁该端口并限制访问源。单个源IP对多个端口发起连接这也是典型的端口扫描模式。我用上一步的按IP统计命令一旦看到某个远端IP的连接数远高于其他IP优先去确认是不是自己的业务出口IP不是的话直接封锁该IP再观察业务是否有异常。从业务正常性角度再反推如果当前连接数总量翻了几倍但业务后台监控显示QPS并没有上涨那多出来的流量一定来自非业务方。此时我会把当前连接统计导出到文件抓几个异常IP然后去查IP信誉确定是否来自IDC扫描段、境外IP池等。综合这几个维度就能区分出是扫描、攻击还是正常流量突增。4. 按场景讲的连接数排查案例4.1 场景一Nginx服务器TIME_WAIT过多导致连接失败这个故障现象是用户反馈系统偶尔卡顿打开页面时好时坏Nginx日志里大量出现upstream timed out。我第一反应就是看Nginx所在服务器的连接状态统计执行ss -s后发现TIME_WAIT竟有3万多。再用脚本按目标IP分组发现大量TIME_WAIT都集中在后端的内网IP上定位到是Nginx频繁向后端发起短连接而每次访问后连接被快速释放。当时的解决方式分了几步第一步在Nginx的upstream配置里开启keepalive 32让Nginx和后端复用长连接减少反复建连第二步开启内核参数net.ipv4.tcp_tw_reuse1允许客户端在安全条件下复用TIME_WAIT连接同时把net.ipv4.ip_local_port_range扩展到10240 65535第三步在后端服务侧也检查了一下连接池配置确保服务端不是主动关闭连接的一方。调完以后TIME_WAIT数量大幅下降接口超时告警也消失了。这里要强调一个容易搞错的点TIME_WAIT的数量多并不总是坏事只有当本地端口耗尽或者连接数影响到新建连接时才需要干预。很多文章一看到TIME_WAIT多就让人改参数但如果没有端口耗尽或者明显性能下降其实可以不用管系统最终会按2MSL时间自动清理。4.2 场景二数据库服务器CLOSE_WAIT堆积引发的应用变慢有一次某个应用的数据库连接数突然爬升但数据库自身的QPS并没有增长数据库服务器的CLOSE_WAIT数量却一直在涨。我在数据库服务器上按进程和状态分了一下组发现大量CLOSE_WAIT集中在来自应用服务器的连接上说明应用服务器在某些场景下没有正确关闭JDBC连接或者连接被异常中断后应用没有感知。我当时建议开发检查连接池配置把连接池的maxLifetime、idleTimeout和validationQuery都设置起来确保能被定期检查和回收。同时在应用日志里查有没有进行中的事务未提交就丢掉了连接。最后定位到是某个批处理逻辑里使用数据库连接后没有放到finally里关闭导致只有连接空闲超时到被池子淘汰时才会释放时间一久CLOSE_WAIT越堆越多应用线程也大量阻塞在拿连接上。CLOSE_WAIT的问题我的经验是百分之九十是代码层面的连接关闭遗漏很少纯粹是内核参数的问题。所以看到CLOSE_WAIT增长不要盲目调内核优先去查哪个进程的连接在堆积顺着PID去定位代码路径。4.3 场景三SYN_RECV暴涨端口扫描还是半连接攻击有一次我在例行巡检时注意到服务器上SYN_RECV连接数超过5000而且目标端口是8020这种不常见的端口业务端口完全没受影响。用ss -ant | grep SYN-RECV | awk {print $4}看了一眼目标端口分布后发现就是单个端口被大量随机源IP尝试连接。从防火墙日志确认了进来的SYN包数量远超正常水平基本确定是端口扫描行为。处理思路是先用防火墙封禁这个端口的外部访问业务本来就不用到这个端口直接拒绝再顺藤摸瓜查一下这些源IP的分布发现来自好几个国家的IP段都指向云平台扫描最后把系统上不需要的监听端口全都清理了一遍并开启了fail2ban对连续多次尝试连接的IP做自动封禁。处理之后SYN_RECV数量就降下来了。如果遇到的是真正的SYN Flood攻击目标端口是业务端口那就不能简单封端口了需要配合流量清洗服务、启用SYN Cookies、调整半连接队列大小等来缓解。这类问题从连接数层面能看到信号但解决起来需要网络架构层面的配合。5. 进阶用监控与自定义脚本持续跟踪连接数5.1 自己写个快速脚本定时记录连接状态排查完一个问题后我的习惯是加一个简单的监控脚本把连接状态按时间维度记录下来以后出问题时有历史数据可以做对比。脚本不复杂就是一个计划任务配一段脚本mkdir -p /var/log/conn_stat */5 * * * * echo $(date %Y-%m-%d %H:%M:%S) $(ss -s | grep -E ^TCP | sed s/^TCP//) /var/log/conn_stat/conn.log这样每5分钟会在日志文件里追加一条当前TCP汇总状态。某天业务反馈变慢时直接打开这个日志看时间轴上的变化就能知道是从哪个时间节点开始连接数升高再结合当时是否有发布操作或流量波动定位速度会快很多。脚本虽然简单但胜在能看到趋势比只抓当前快照靠谱得多。更进一步我还会监控进程级别的文件描述符使用情况*/5 * * * * cat /proc/PID/status | grep -E FDSize|Threads /var/log/conn_stat/app_fd.log比如Nginx的PID、Java应用的PID都有自己的fd增长曲线。连接数高不可怕怕的是连接数高但fd已经逼近上限故障随时可能触发。5.2 借助Prometheus、node_exporter做长期可视化如果服务器较多单纯靠写日志不是长久之计。我在维护多台机器的环境里会用Prometheus加node_exporter拉取网络指标。node_exporter自带node_sockstat_TCP_alloc、node_sockstat_TCP_tw、node_netstat_Tcp_CurrEstab等指标能直接采集当前TCP连接状态。一条典型的PromQL查询连接数趋势sum(node_netstat_Tcp_CurrEstab) by (instance)Grafana上配上面板之后能在连接数异常上涨时快速看到是哪些机器在涨、是否和告警时间吻合。相比手动执行命令这个做法更像一个正儿八经的监控体系适合需要值守的环境。配置告警规则时我通常按状态分别设置阈值例如ESTABLISHED超过基线200%、TIME_WAIT大于20000、SYN_RECV大于1000持续5分钟以上就告警。阈值并不是拍脑袋定的而是看了一段时间的正常运行数据后按3到5倍正常量级来设置的避免日常小波动就频繁骚扰。有一点值得提醒node_exporter采集的是主机层面的状态只能看总量无法细到某个四元组。如果需要更细的分析还是要靠ss导出的连接快照或者用 eBPF 工具捕获连接事件。监控做的是“报警”定位做的是“排障”两者不能互相替代。5.3 其他实用排查工具组合除了ss和netstat我在不同场景下还会用到几个工具组合起来分析连接数会更顺手。lsof用来精确查看某个进程打开的所有网络连接典型的命令是lsof -i -P -n | grep nginx-P禁用端口名解析-n禁用域名解析执行起来更快输出也更明确。连接数异常时通过lsof能直接看出哪个进程和哪个远端IP有连接比ss -p的信息更细腻一点。tcpdump适合做抓包分析比如怀疑有IP在持续连接但连接建立后立即发数据可以抓一下包看载荷特征。不建议在流量大的生产主机上长时间全量抓包但配合过滤条件短时间抓取通常就能拿到关键信息。以前排查一个被中马的情况就是通过 tcpdump 发现服务器往外连了一个海外IP的443端口才定位到问题。nethogs是一个按进程显示实时带宽的工具配合连接数查看能一眼看到哪个进程在大量占带宽和建立连接。不过它在高流量下也有点吃资源一般只在已经怀疑到某个具体进程时才用。这些工具我统一的观点是会用不熟没关系但至少知道每个工具能回答什么问题比如ss回答“当前有什么连接”lsof回答“这个进程打开了哪些连接”tcpdump回答“连接里到底在传什么”nethogs回答“哪个进程在抢占带宽”。排查时把这几个答案拼起来全貌就出来了。6. 常见问题排查速查表为了日常使用方便我把常见的连接数异常状态、可能原因和处理方向整理成了一个速查表放在服务器运维文档里每次同事遇到类似问题也先丢这个表给他们。连接状态或现象可能原因初步处理方向TIME_WAIT 数量大大量短连接主动关闭方没有复用连接服务端/客户端开启 keepalive调整 tw_reuse扩大端口范围CLOSE_WAIT 持续增长应用没有正确关闭连接排查业务代码连接管理检查连接池配置SYN_RECV 异常升高半连接队列满、SYN攻击、后端accept慢调整 tcp_max_syn_backlog、启用 syncookies、检查后端队列ESTABLISHED 突然跌零服务进程退出、端口停止监听、防火墙拦截检查进程、监听端口、防火墙规则某个IP连接数异常高可能是业务出口IP或攻击源确认IP归属必要时封禁并抓包分析端口耗尽报错 “Cannot assign requested address”客户端短连接过多本地端口范围不够扩大 ip_local_port_range改造连接模式连接时通时不通conntrack表满nf_conntrack_max 到达上限调整 conntrack 配置或减小超时时间排查顺序我总结了三步第一步看ss -s掌握全局状态第二步按状态、源IP、目标端口聚合定位异常连接特征第三步顺着PID和抓包看具体是哪个进程在做什么。这三步走完大多数连接数问题都能找到方向。有个细节容易被忽略查看连接数时最好加上时间维度。比如同样一条ss -s5分钟前的输出和现在的输出相比较才能判断数字是稳定缓慢增长还是突然飙升。我排查时会把第一次看到的输出保存下来连续看几次才下结论。不要一上来看到某个数字偏大就急着调优很多状态在TCP正常生命周期里本来就会同时大量存在。7. 一点个人经验之谈写到这里很多人可能会觉得“查看网络连接数”就是一条命令的事但我在实际排查了各种连接类故障后最大的体会是连接数只是表象真正的功夫在于结合业务形态去判断这些数字是否合理。同样是2万个TIME_WAIT在一个高并发的短连接API服务上可能完全正常在一个长连接业务上就是严重异常。所以不要迷信网上的“标准值”要把自己服务器的基线建起来平时的正常范围是多少数值翻了多少倍这比绝对值更有参考价值。还有一个经验是改内核参数前一定先备份而且每次只改一项观察一段时间。有些参数之间是有关联的比如tcp_tw_reuse和连接复用调的激进可能影响需要四元组唯一性的场景。以前我就因为同时改了两三个参数出问题后无法确定是哪个改动引入的来回回滚花了不少时间。现在我的原则是能通过业务层和应用层解决的优先改代码实在需要动内核的小步调留回滚点。再分享一个小技巧把ss的输出习惯加上-n大部分情况下都别加-r或域名解析。排查现场域名的反解析不仅慢还可能因为内网DNS异常导致命令卡住反而误事。IP地址虽然看起来不直观但配合已知的服务端口足够判断是谁连谁了。服务器网络连接数这个指标属于那种看起来简单、但每个现场都可能有新花样的东西。希望你看完这篇之后至少知道下次遇到连接数异常时该怎么一步步查下去少踩几个坑。