Python案例:如何高效统计网络中连接数量?从基础到实战全解析
目录导读
- 问题背景:为什么需要统计连接数量?
- 核心概念:TCP/UDP连接与计数原理
- 基础方法:利用
psutil库快速统计系统连接数 - 进阶实战:构建实时连接监控脚本
- 网络爬虫场景:统计HTTP并发请求连接
- 性能优化:内存与效率平衡策略
- 常见问题问答(FAQ)
问题背景:为什么需要统计连接数量?
在日常运维、网络安全监控或高并发系统开发中,统计设备或程序当前的网络连接数量是基础且关键的需求。

- 排查DDoS攻击:异常突增的连接数可能是攻击信号。
- 优化服务器资源:连接数过多可能耗尽系统文件描述符。
- 爬虫开发:控制并发连接避免被目标服务器封禁。
Python凭借其丰富的库生态,成为实现这一功能的理想语言,但许多开发者只停留在“读取/proc/net”的浅层,本文将带您从基础到高级,掌握真实场景下统计连接数量的完整方法论。
核心概念:TCP/UDP连接与计数原理
要精准计数,需理解不同协议的状态机,一条TCP连接的生命周期包含ESTABLISHED、CLOSE_WAIT、TIME_WAIT等状态,统计时需明确:
- 目标状态:仅统计已建立的连接?还是包含正在关闭的?
- 协议类型:TCP vs UDP(UDP无连接但仍有套接字占用)。
- 源/目的过滤:按IP或端口筛选。
Linux系统下,/proc/net/tcp和/proc/net/udp文件存储了原始连接表,Python通过解析这些文件获得原始数据,而psutil库则封装了跨平台的抽象接口。
基础方法:利用psutil库快速统计系统连接数
安装(如未安装):
pip install psutil
基础代码示例:
import psutil
def count_total_connections():
connections = psutil.net_connections()
return len(connections)
print(f"当前系统共有 {count_total_connections()} 个网络连接")
按状态过滤:
def count_by_state(state='ESTABLISHED'):
conns = psutil.net_connections()
return len([c for c in conns if c.status == state])
print(f"已建立连接数: {count_by_state('ESTABLISHED')}")
print(f"TIME_WAIT连接数: {count_by_state('TIME_WAIT')}")
按进程筛选(例如统计Python进程):
for proc in psutil.process_iter(['pid', 'name']):
if 'python' in proc.info['name']:
p_conns = proc.connections()
print(f"进程 {proc.info['pid']} 连接数: {len(p_conns)}")
注意:
psutil.net_connections()需要管理员/root权限才能获取所有连接信息,否则只返回当前用户进程的连接。
进阶实战:构建实时连接监控脚本
场景:每5秒记录一次系统连接状态变化,当ESTABLISHED连接超过阈值时报警。
import psutil
import time
import json
THRESHOLD = 1000 # 阈值
def monitor_connections(interval=5):
prev_count = 0
while True:
conns = psutil.net_connections()
current_count = len(conns)
est_count = len([c for c in conns if c.status == 'ESTABLISHED'])
# 增量变化
delta = current_count - prev_count
if delta > 0:
print(f"[{time.strftime('%H:%M:%S')}] 连接增加 {delta},当前总数: {current_count}")
# 阈值告警
if est_count > THRESHOLD:
print(f"⚠️ 警告: ESTABLISHED连接 {est_count} 超过阈值 {THRESHOLD}")
# 将数据写入日志 (JSON格式)
log_data = {
"timestamp": time.time(),
"total_connections": current_count,
"established": est_count
}
with open("connection_log.json", "a") as f:
f.write(json.dumps(log_data) + "\n")
prev_count = current_count
time.sleep(interval)
if __name__ == "__main__":
monitor_connections()
运行效果:
脚本在后台运行,会将连接变化实时打印并记录,当连接数突增时,管理员可快速定位异常。
网络爬虫场景:统计HTTP并发请求连接
在爬虫开发中,需要控制并发连接避免被封,我们可以统计当前requests或aiohttp发起的连接数。
基于requests.Session的统计(同步):
import requests
from threading import current_thread
active_connections = {}
class MonitoredSession(requests.Session):
def send(self, request, **kwargs):
thread_name = current_thread().name
active_connections[thread_name] = active_connections.get(thread_name, 0) + 1
try:
response = super().send(request, **kwargs)
finally:
active_connections[thread_name] -= 1
if active_connections[thread_name] == 0:
del active_connections[thread_name]
return response
# 使用示例
session = MonitoredSession()
session.get("https://example.com")
print(f"当前活跃连接数: {sum(active_connections.values())}")
基于aiohttp的异步统计:
import aiohttp
import asyncio
active_conns = 0
async def fetch(url, session):
global active_conns
active_conns += 1
async with session.get(url) as resp:
data = await resp.text()
active_conns -= 1
return data
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch("https://example.com", session) for _ in range(10)]
await asyncio.gather(*tasks)
print(f"最终活跃连接数: {active_conns}")
asyncio.run(main())
原理:通过包装HTTP库的发送方法,在请求开始前计数、结束后减一,即可实时掌控并发连接数。
性能优化:内存与效率平衡策略
当系统连接数极大(例如10万以上)时,psutil.net_connections()会消耗大量CPU和内存,优化建议:
-
使用
procfs直接解析(仅Linux):def count_connections_fast(): with open('/proc/net/tcp', 'r') as f: lines = f.readlines() return len(lines) - 1 # 去除头部这种方法绕过Python对象创建,速度提升10倍以上。
-
按需过滤:不要一次性加载所有连接,而是使用
psutil.net_connections(kind='tcp')指定协议。 -
采样间隔:监控脚本中采样间隔不要小于1秒,避免频繁IO。
-
使用C扩展(如
fastproc库):专门优化过的解析库。
常见问题问答(FAQ)
Q1:为什么我psutil.net_connections()返回空列表?
A:通常因为权限不足,在Linux下尝试sudo python script.py,或在Windows中以管理员身份运行。
Q2:统计到的连接数比netstat -an少几个?
A:psutil会过滤掉UNIX socket连接(进程间通信),而netstat默认包含它们,如需统计UNIX socket,使用psutil.net_connections(kind='unix')。
Q3:如何统计特定端口(如8080)的连接数?
A:遍历连接对象,检查laddr.port或raddr.port:
conns = psutil.net_connections() port_8080_conns = [c for c in conns if c.laddr.port == 8080] print(len(port_8080_conns))
Q4:在Kubernetes容器中统计连接需要注意什么?
A:容器内/proc文件系统是隔离的,只能看到本容器的连接,如果需要统计宿主机全局连接,需要挂载宿主机的/proc到容器,或使用hostPID: true。
Q5:统计UDP连接与TCP有何不同?
A:UDP无连接状态,但每个套接字仍被计数,统计时psutil.net_connections(kind='udp')即可,UDP连接数一般远少于TCP。