Python案例如何统计连接数量?

wen python案例 70

Python案例:如何高效统计网络中连接数量?从基础到实战全解析

目录导读

  1. 问题背景:为什么需要统计连接数量?
  2. 核心概念:TCP/UDP连接与计数原理
  3. 基础方法:利用psutil库快速统计系统连接数
  4. 进阶实战:构建实时连接监控脚本
  5. 网络爬虫场景:统计HTTP并发请求连接
  6. 性能优化:内存与效率平衡策略
  7. 常见问题问答(FAQ)

问题背景:为什么需要统计连接数量?

在日常运维、网络安全监控或高并发系统开发中,统计设备或程序当前的网络连接数量是基础且关键的需求。

Python案例如何统计连接数量?

  • 排查DDoS攻击:异常突增的连接数可能是攻击信号。
  • 优化服务器资源:连接数过多可能耗尽系统文件描述符。
  • 爬虫开发:控制并发连接避免被目标服务器封禁。

Python凭借其丰富的库生态,成为实现这一功能的理想语言,但许多开发者只停留在“读取/proc/net”的浅层,本文将带您从基础到高级,掌握真实场景下统计连接数量的完整方法论

核心概念:TCP/UDP连接与计数原理

要精准计数,需理解不同协议的状态机,一条TCP连接的生命周期包含ESTABLISHEDCLOSE_WAITTIME_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并发请求连接

在爬虫开发中,需要控制并发连接避免被封,我们可以统计当前requestsaiohttp发起的连接数。

基于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和内存,优化建议:

  1. 使用procfs直接解析(仅Linux)

    def count_connections_fast():
        with open('/proc/net/tcp', 'r') as f:
            lines = f.readlines()
        return len(lines) - 1  # 去除头部

    这种方法绕过Python对象创建,速度提升10倍以上。

  2. 按需过滤:不要一次性加载所有连接,而是使用psutil.net_connections(kind='tcp')指定协议。

  3. 采样间隔:监控脚本中采样间隔不要小于1秒,避免频繁IO。

  4. 使用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.portraddr.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。

抱歉,评论功能暂时关闭!