用 Rust 构建一个实时的终端网络流量监控器
前言
当局域网网速突然变慢时,我们不仅想知道“网速慢了”,更想知道“是谁在占用带宽”。(实验室的网总是不好,用这个小工具看看到底是谁在占用带宽,当做复习 Rust 的练手)
虽然 Linux 上有经典的 iftop,但它的界面有些古老。而 btop 虽然界面炫酷,但它主要监控系统资源(CPU/内存),在网络流量的具体来源分析上功能有限。
目前可以实现的功能如下,未来如果需要什么功能再增加
- 实时流量监控:基于
libpcap的底层抓包。 - 绘制图表:使用
Ratatui绘制波形图,类似于 Btop++风格 - 统计网速:基于滑动窗口算法计算 1 分钟平均网速,过滤瞬时抖动。
- 自动日志:支持流量快照记录,并自动轮转日志文件。
- 识别主机名和厂商:支持 DNS 反向解析和 MAC 地址厂商识别。
代码仓库: https://github.com/ssfortynine/net_monitor
目前识别主机名和厂商,通过 mDNS识别只能识别一些简单设备(本机以及路由器),其他的设备设置了防火墙,根据这个方法无法读取到主机名。
- 尝试使用了一些网络工具
nmlookup也只能查找到小部分,大部分 PC 机查找不到
识别厂商名比较简单,只需要识别 MAC 地址的前缀即可,但是 OUI 数据集不好搜索,现在的 MAC 地址前缀不只是前 6 位,文件夹下保存了 Nmap MAC 的前缀
虽然可以识别 Vendor ,但是我们实验室使用的机子都是一家厂商,所以 MAC 地址也都一样,所以我就把这个功能搁置了,感觉没有什么用
Rust 库选择
- 核心库:
pcap / pnet: 处理底层网络包捕获。ratatui: 目前 Rust 生态最强大的 TUI 库,用于绘制界面。crossterm: 处理终端输入和原始模式。
- 辅助库:
log4rs: 处理日志轮转(限制文件大小、自动删除旧日志)。dns-lookup: 解析 IP 对应的域名。chrono: 时间处理
架构设计:生产者-消费者模型
为了保证界面渲染流畅(500ms 刷新一次)且不丢失任何一个数据包,系统采用了经典的线程分离设计:
- 捕获线程(Producer):置于后台,处于混杂模式(Promiscuous Mode)监听网卡。它负责解析数据包,并累加每个 IP 产生的字节数。
- UI 线程(Consumer):主线程负责定时从共享内存读取数据。它计算速率、更新历史采样点,并最终渲染图形。
- 共享状态(SharedStats):仅保存增量,UI 线程每读取一次便将其清零,简化了锁的竞争时间。
关键算法:基于滑动窗口的速率统计
简单的“瞬时速率”会导致图表剧烈跳动,难以观察长期趋势。我在系统中引入了 IpHistory 结构体,利用双端队列(VecDeque)实现了一个滑动窗口:
1 | pub struct IpHistory { |
底层逻辑:从字节流到 IP 地址
数据采集模块的核心在于对网络协议栈的逐层拆解。利用 pnet 库解析数据包:
1 | // 1. 解析以太网帧 |
TUI 视觉优化
终端的字符像素通常是 1x1,绘制曲线会有严重的锯齿。我选用了 Ratatui 的 Marker::Braille(盲文)。盲文符号由 8 个点组成,可以将一个字符单元拆分为 2x4 的微小“像素点”。
这个部分是参考 Btop++ 的 UI风格
同时,针对流量排行表(Top Talkers)设置动态颜色:
- 红色:当平均网速超过 1MB/s 时,提醒用户该设备正在进行大流量操作。
- 绿色:轻量级流量显示。
参考资料
OUI 数据库的来源: https://github.com/Ringmast4r/OUI-Master-Database?tab=readme-ov-file
