构建实时网络速度监控面板:Python Flask + SSE 技术详解

构建实时网络速度监控面板:Python Flask + SSE 技术详解

本文将带你一步步构建一个实时网络速度监控面板,使用 Python Flask 后端和 Server-Sent Events (SSE) 前端技术。

项目概述

这个网络监控工具可以实时显示每个网络接口的下载和上传速度,类似于 Web 版本的 iftop 或 nload 命令。

功能特点

  • 实时监控所有活跃网络接口
  • 自动过滤回环接口和无效接口
  • 按下载速度排序显示
  • 同时显示字节每秒和比特每秒
  • 简洁美观的 Web 界面

技术栈

  • 后端: Python + Flask + psutil
  • 前端: HTML + CSS + JavaScript (SSE)
  • 通信: Server-Sent Events (SSE)

完整代码实现

后端代码 (app.py)

python 复制代码
import time
import json
import psutil
from flask import Flask, Response, render_template_string

app = Flask(__name__)

def collect(interval: float = 1.0):
    """
    收集网络接口统计信息
    interval: 采样间隔时间(秒)
    """
    # 获取第一次网络IO计数
    prev = psutil.net_io_counters(pernic=True)
    time.sleep(interval)
    # 获取第二次网络IO计数
    curr = psutil.net_io_counters(pernic=True)
    stats = psutil.net_if_stats()

    def is_active(name: str) -> bool:
        """检查网络接口是否活跃且不是回环接口"""
        st = stats.get(name)
        if not st or not st.isup:
            return False
        # 过滤回环接口
        if name.lower() in ("lo", "loopback", "loopback pseudo-interface 1"):
            return False
        return True

    summary = {"down_bps": 0.0, "up_bps": 0.0, "ifaces": []}
    names = set(prev.keys()) & set(curr.keys())

    for name in sorted(names):
        if not is_active(name):
            continue
        
        # 计算速度(字节/秒)
        p = prev[name]
        c = curr[name]
        down = max(c.bytes_recv - p.bytes_recv, 0) / interval
        up = max(c.bytes_sent - p.bytes_sent, 0) / interval
        
        summary["down_bps"] += down
        summary["up_bps"] += up
        summary["ifaces"].append({"name": name, "down_bps": down, "up_bps": up})

    # 按下载速度降序排序
    summary["ifaces"].sort(key=lambda x: x["down_bps"], reverse=True)
    return summary

def format_rate(bytes_per_sec: float) -> str:
    """格式化速度显示"""
    bits_per_sec = bytes_per_sec * 8
    
    def human(n, unit):
        for s in ["", "K", "M", "G", "T"]:
            if n < 1024:
                return f"{n:,.1f} {s}{unit}"
            n /= 1024
        return f"{n:,.1f} P{unit}"
    
    return f"{human(bytes_per_sec, 'B/s')} ({human(bits_per_sec, 'bps')})"

@app.route("/metrics")
def metrics():
    """SSE 流端点,每秒推送一次网络数据"""
    def stream():
        while True:
            data = collect(1.0)
            yield f"data: {json.dumps(data)}\n\n"
    
    return Response(stream(), mimetype="text/event-stream")

# HTML 模板
INDEX_HTML = """
<!doctype html>
<meta charset="utf-8" />
<title>Net Speed Panel</title>
<style>
  body { font-family: system-ui, sans-serif; max-width: 860px; margin: 24px auto; }
  header { display: flex; align-items: baseline; justify-content: space-between; }
  .total { font-size: 18px; margin: 12px 0; }
  table { width: 100%; border-collapse: collapse; }
  th, td { padding: 8px; border-bottom: 1px solid #eee; text-align: left; }
  th { background: #fafafa; }
  .down { color: #0b6; }
  .up { color: #06b; }
  .muted { color: #888; }
</style>
<header>
  <h1>网络速度监控面板</h1>
  <span class="muted">每秒刷新</span>
</header>
<div class="total" id="total">总计 加载中...</div>
<table>
  <thead>
    <tr><th>网卡</th><th>下行</th><th>上行</th></tr>
  </thead>
  <tbody id="tbody"></tbody>
</table>
<script>
  function human(n, unit) {
    const units = ['', 'K', 'M', 'G', 'T'];
    let i = 0;
    while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; }
    return `${n.toFixed(1)} ${units[i]}${unit}`;
  }
  
  function formatRate(bytesPerSec) {
    const bps = bytesPerSec * 8;
    return `${human(bytesPerSec, 'B/s')} (${human(bps, 'bps')})`;
  }
  
  const total = document.getElementById('total');
  const tbody = document.getElementById('tbody');
  const es = new EventSource('/metrics');
  
  es.onmessage = (e) => {
    const data = JSON.parse(e.data);
    total.textContent = `总计  ↓ ${formatRate(data.down_bps)}    ↑ ${formatRate(data.up_bps)}`;
    tbody.innerHTML = '';
    
    data.ifaces.forEach(({name, down_bps, up_bps}) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `<td>${name}</td><td class="down">↓ ${formatRate(down_bps)}</td><td class="up">↑ ${formatRate(up_bps)}</td>`;
      tbody.appendChild(tr);
    });
  };
</script>
"""

@app.route("/")
def index():
    return render_template_string(INDEX_HTML)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)

核心原理详解

1. 网络速度计算原理

python 复制代码
def collect(interval: float = 1.0):
    prev = psutil.net_io_counters(pernic=True)  # 第一次采样
    time.sleep(interval)                         # 等待指定间隔
    curr = psutil.net_io_counters(pernic=True)  # 第二次采样
    
    # 计算速度 = (第二次计数 - 第一次计数) / 时间间隔
    down = (c.bytes_recv - p.bytes_recv) / interval
    up = (c.bytes_sent - p.bytes_sent) / interval

这种方法通过两次采样之间的差值来计算瞬时速度,比直接读取瞬时值更准确。

2. Server-Sent Events (SSE) 技术

SSE 允许服务器主动向客户端推送数据,非常适合实时监控场景:

python 复制代码
@app.route("/metrics")
def metrics():
    def stream():
        while True:
            data = collect(1.0)  # 每秒收集一次数据
            yield f"data: {json.dumps(data)}\n\n"  # SSE 格式
    
    return Response(stream(), mimetype="text/event-stream")

前端通过 EventSource API 接收数据:

javascript 复制代码
const es = new EventSource('/metrics');
es.onmessage = (e) => {
    const data = JSON.parse(e.data);
    // 更新界面
};

3. 速度单位换算

python 复制代码
def format_rate(bytes_per_sec: float) -> str:
    bits_per_sec = bytes_per_sec * 8  # 转换为比特
    
    def human(n, unit):
        # 自动选择合适的单位 (B, KB, MB, GB, TB)
        for s in ["", "K", "M", "G", "T"]:
            if n < 1024:
                return f"{n:,.1f} {s}{unit}"
            n /= 1024
        return f"{n:,.1f} P{unit}"
    
    return f"{human(bytes_per_sec, 'B/s')} ({human(bits_per_sec, 'bps')})"

部署和运行

安装依赖

bash 复制代码
pip install flask psutil

运行应用

bash 复制代码
python app.py

访问 http://localhost:5000 即可看到监控面板。

总结

通过这个项目,我们学习了:

  • 使用 psutil 库获取系统网络信息
  • SSE 技术的原理和实现方式
  • 实时数据计算的差分方法
  • 前后端分离的 Web 应用架构

这个网络监控工具不仅实用,也是学习实时 Web 应用开发的优秀示例。你可以基于这个基础继续添加更多功能,打造属于自己的系统监控工具。

希望这个教程对你有所帮助!

相关推荐
通信数码研究院5 小时前
2026户外监控选什么?TOP5场景适配榜单:五类安装环境与产品选型参考
网络
打工仔折腾 AI5 小时前
网易UU远程实测:手机控电脑做Python开发和Agent调试的真实体验
人工智能·后端·python·智能手机·性能优化·电脑·ai agent 实战
谢亮_vipxieliang5 小时前
容器日志收集与管理:从 stdout 规范到 ELK/Loki 落地
运维·网络·人工智能·elk·docker·容器
轩轶子5 小时前
Python CLI Todo:一次数据结构错误引发的 Bug
数据结构·python·bug
“AI国潮设计-小江”6 小时前
【AIGC实战】Python+SDXL打造潮汕国潮IP:英歌舞麻将的自动化生成与商用思路
开发语言·人工智能·python·prompt·aigc
QYRdata6 小时前
年复合增长率19.6% 数据隐私安全软件赛道领跑新兴科技
网络·科技·服务发现
zh路西法6 小时前
【上手一只MicroDuck】:(一)从机器人结构到强化学习训练框架
python·强化学习·huggingface·ppo·mujoco·microduck
半仙白桑6 小时前
内核篇第十三讲:system-V消息队列
java·linux·网络
XUEYUAN52126 小时前
路由跳数与地理位置校验:为什么 IP 归属地和实际出口位置不一致
网络·网络协议·tcp/ip
alphaTao6 小时前
LeetCode 每日一题 2026/9/21-2026/9/27
python·算法·leetcode