ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java网络流量分析实战:Pcap4J抓包到WebSocket实时监控

Java网络流量分析实战:Pcap4J抓包到WebSocket实时监控 简介这是一份面向高校计算机网络课程设计场景的Java网络流量分析软件项目以Java编写后台核心逻辑通过Web前端完成实时监控与数据可视化适用于远程服务器或无图形界面的Linux主机等环境。项目针对数据传输安全与长期运行稳定性做了专门设计并配有课程设计报告和构建脚本。压缩包共76个文件大小约11.32MB主要包含27个Java源码、15个JavaScript、9个JSX组件、Gradle构建配置、JAR依赖包及证书/密钥文件等源码、文档、配置分层清晰。目前已有462人学习下载。读者可借此掌握网络流量采集、WebSocket推送、前端图表展示及TLS加密通信等完整实现思路并可直接基于Gradle工程运行调试是课程设计与毕设参考的实用资料。1. Java 网络流量分析为什么我推荐用 Web 方式而不是桌面端做完这套 Java 网络流量分析课程设计我最大的感触是流量监控真正的难点不在抓包而在把抓到的数据安全地送到远端的浏览器里。这个项目用 Java 做后台用 Web 客户端做展示层目标机即使没有图形界面、处于远程位置也能通过浏览器实时看到监控结果相比普通的桌面端抓包工具它多解决了远程展示和传输安全这两件麻烦事。资源包编号 100010394里面带着完整 Gradle 工程、源码、打包好的 traffic_analysis-1.0.jar 和课程设计报告书。适合计网课程设计要交报告的同学、想在无界面服务器上做流量监控的 Java 后端开发以及刚入门想接触协议解析实战的人。2. 系统架构与链路设计抓包、上报、浏览器展示的数据通路这套项目的整体结构不复杂核心是一条单向数据流水线网卡数据进入 Java 进程完成协议解析和统计之后推送到浏览器端展示。理解这条链路比读懂任何一段代码都重要后面改参数、排查问题都以它为依据。2.1 三层架构采集、分析、展示的职责边界常规实现会把代码拆成采集层、分析层和展示层。采集层是唯一直接和系统网卡打交道的部分负责从网卡拿到原始数据帧转换成 Java 的 Packet 对象分析层负责解析以太网、IPv4/IPv6、TCP/UDP维护连接表和流量统计展示层不参与抓包逻辑只负责把分析结果通过 WebSocket 推给浏览器。模块职责关键技术点采集层网卡枚举、过滤规则、实时抓包libpcap / WinPcap / Npcap Pcap4J分析层协议解析、连接跟踪、聚合统计纯 Java 实现无本地库依赖展示层WebSocket 推送、Web UI、图表刷新Java-WebSocket / JSR-356Web 前端为什么要这样拆因为采集层最高频的操作是每秒钟成百上千个回调展示层的数据是每秒定期批量推送一次两边的频率和容错策略完全不同。拆开之后抓包线程只做一件事推送线程只做另一件事参数可以各自调。分析层是纯 Java 逻辑不依赖任何系统库方便在单元测试里直接跑也为第 6 章要做的离线回放验证埋下伏笔。经常有人答辩时被问为什么不用纯 Java 实现抓包原因很简单Java 本身没有能力直接读取网卡数据帧必须依赖操作系统提供的 pcap 接口。这个项目里的 Java 代码通过 Pcap4J 的 JNA 绑定去调各平台上的 libpcap / WinPcap / Npcap 动态库Java 侧只负责拿到 Packet 对象之后的业务处理。这也是“跨平台”能成立的前提抓包能力交给系统库业务逻辑全部留在 Java 这一层。2.2 数据流一个包从网卡到浏览器经历了什么把这条链路拆开看顺序是这样的网卡收到数据帧内核驱动把帧交给 libpcap / Npcap这一步在 Linux 上依赖 libpcap在 Windows 上依赖 Npcap。Pcap4J 把原生数据帧封装成 Packet 对象通过 PacketListener 回调给 Java 侧。分析层调用 packet.get(IpV4Packet.class)、get(TcpPacket.class) 做协议匹配提取五元组写入连接表 ConcurrentHashMap。一个定时任务按配置的间隔默认 1 秒扫描连接表计算每个连接的包数、字节数、上下行速率生成统计快照。统计快照序列化成 JSON通过 WebSocket 推送浏览器端收到后更新图表。这里要注意线程模型抓包回调线程是 pcap 的读线程推送线程是 ScheduledExecutorService 的调度线程两边不能互相阻塞。如果你把推送逻辑直接写在抓包回调里网络突发时前端一卡抓包线程跟着堵丢包就是必然的。我一般会在回调里只做统计写入不做任何 IO 操作。推送下来的 JSON 大概长这样{ time: 1719907200000, totalBytes: 104857600, packets: 204800, activeConnections: 128, topTalkers: [ {src: 192.168.1.10:52341, dst: 93.184.216.34:443, bytes: 409600} ] }time 是毫秒时间戳前端用来画横轴activeConnections 是当前连接表里处于活跃状态的连接数topTalkers 取流量最大的几条连接方便快速定位谁在占带宽。调试时我习惯把这条 JSON 打到日志里前端任何“图表不动”的问题先确认这一层有没有输出就能判断是后端没抓到包还是推送断了。2.3 安全传输certs 目录与证书体系为什么这里要坚持用 WebSocket 而不是 HTTP 轮询因为后端要实时推数据轮询只能靠前端每秒请求一次连接开销大、延迟高WebSocket 建立一条长连接后服务端可以主动推送单条连接上承载每秒一条的统计数据很轻松。如果担心断线前端做心跳重连即可。同时这套项目专门带了一个 certs 目录原因是设计目标里有“远程目标机”这一条。抓包数据本身包含大量敏感信息DNS 请求域名、目标 IP、URL 路径、报文长度。如果这些数据走明文 HTTP 送到浏览器等于把内网流量透视结果暴露在网络上。常见做法是给 WebSocket 套一层 TLS也就是 wss://。后端启动时读取 certs 下的密钥库构建 SSLContext用带 SSL 的 WebSocketServer 监听安全端口。开发环境用 keytool 生成自签 PKCS12 密钥库就够用生产环境建议换受信任 CA 签发的证书。注意自签证书在浏览器端会有证书警告内网调试时点一次“继续前往”即可但在公网环境必须用正规证书。配套的常用配置.txt 一般会收敛成下面这几项按需调整配置项常见取值说明listen.port8443WebSocket 服务端口避开 80/443 的低端口权限问题capture.deviceeth0 或 \Device\NPF_{...}要监控的网卡名Windows 上先枚举再填bpf.filtertcp or udp伯克利包过滤表达式减少无效包aggregate.interval1000统计聚合周期单位毫秒keystore.pathcerts/server.p12Java 读取的密钥库路径keystore.password由资源文档决定密钥库口令建议放环境变量而不是明文我见过不少同学把 capture.device 配错Linux 上写 eth0 没问题Windows 上不先枚举网卡直接凭印象填名字大概率抛“找不到网卡”的异常。配置解析建议做成“留空时自动选择第一个非回环网卡”首跑体验会好很多。3. Gradle 构建与项目启动从源码到 traffic_analysis-1.0.jar3.1 项目结构src/main、bin、certs、常用配置.txt 分别是什么拿到压缩包解压后先看目录结构不要急着开 IDE。这套资源是标准 Gradle 工程目录里几个主要部分如下100010394-基于Java实现网络流量分析软件/ ├── build.gradle ├── settings.gradle ├── gradlew / gradlew.bat ├── gradle/ ├── src/main/ ├── bin/ ├── certs/ ├── 常用配置.txt ├── traffic_analysis-1.0.jar └── README.md / TASK.MD / 计网课程设计报告书.pdfbuild.gradle 和 settings.gradle 是 Gradle 工程的配置文件gradlew 和 gradlew.bat 分别是 Linux/macOS 和 Windows 下的 Gradle 启动脚本它们会按 gradle/wrapper 里记录的版本自动下载对应的 Gradle 发行版避免本机版本不一致导致的构建失败。src/main 是全部 Java 源码所在地。bin 目录里是启动脚本certs 放证书和密钥库traffic_analysis-1.0.jar 是已经构建好的打包产物不想重新编译可以直接用。比较容易被忽略的是压缩包里同时有 TASK.MD 和计网课程设计报告书.pdf。TASK.MD 是任务说明报告书是供答辩使用的文档里面通常包含设计思路、模块划分和测试结果。如果你要把这套工程改成自己的课程设计报告书是很好的改写蓝本。LICENSE 文件决定了代码的再分发边界拷贝给别人之前先看一眼。3.2 构建配置build.gradle 里的依赖与参数打开 build.gradle核心配置一般长这样具体版本号以资源里的 gradle 依赖为准这里用占位符表示plugins { id java id application } group com.traffic version 1.0 repositories { mavenCentral() // 国内网络建议追加 maven { url https://maven.aliyun.com/repository/public } } dependencies { implementation org.pcap4j:pcap4j-core:2.x.x implementation org.pcap4j:pcap4j-packetfactory-static:2.x.x implementation org.java-websocket:Java-WebSocket:1.x.x } application { mainClass com.traffic.Main }Pcap4J 的两个依赖分别负责核心抓包能力和静态包工厂。pcap4j-packetfactory-static 的意义在于把解析器统一注册它决定packet.get(IpV4Packet.class)这种写法能不能工作漏掉这个依赖最常见的报错是 PacketFactory not set。Java-WebSocket 负责 WebSocket 服务端。mainClass 是启动入口如果你的源码结构和上面不同改成自己主类的全限定名。构建命令就一条./gradlew clean build第一次执行时gradlew 会根据 wrapper 配置从远程仓库下载 Gradle 发行版和依赖包耗时取决于网络建议先配置国内镜像。构建完成后产物会出现在 build/distributions 或 build/libs 下项目里自带的 traffic_analysis-1.0.jar 就是这种流程打出来的。Gradle 构建失败的常见情况是依赖下载超时。改了仓库地址后需要执行一次./gradlew build --refresh-dependencies否则 Gradle 可能继续用缓存里的旧依赖。另外Java 版本要和 build.gradle 里的编译级别对齐。现在不少课程设计环境还是 JDK 8而较新的 Pcap4J 版本要求 JDK 8如果本机装了 JDK 17一般没问题但遇到 invalid source release 报错就要去检查 sourceCompatibility 配置。3.3 启动bin 脚本与常用配置.txtGradle 的 application 插件默认会在 build/distributions 里生成可运行压缩包bin 目录下的脚本会帮用户配好 classpath。如果不想解压到系统里直接执行脚本也行bin/start.sh --config 常用配置.txtWindows 上对应的是 bin/start.bat。如果你更习惯直接跑 jar命令是java -jar traffic_analysis-1.0.jar --config 常用配置.txt这里要解释一下配置文件的优先级逻辑。抓包涉及网卡名、过滤器、推送端口这些和环境强相关的参数把它们收敛到“常用配置.txt”比改代码重新编译省事得多。配置解析建议做成命令行传参 配置文件 代码默认值。这样首次启动什么都不填也能起来只是默认监听 8443 端口。这里有一条最容易忽略的如果本机 JAVA_HOME 没配好gradlew 会直接报“找不到 java”或者“无效的 JAVA_HOME”。先执行java -version确认 Java 可用再执行 gradlew能省掉一半的启动排错时间。Linux 服务器上跑长任务别直接关终端用 nohup 挂后台并输出日志nohup java -jar traffic_analysis-1.0.jar --config 常用配置.txt traffic.log 21 看到“WebSocket server started at wss://0.0.0.0:8443”类似的日志说明服务已经起来了。这时浏览器打开 Web 页面如果连接失败第一件事检查端口有没有被防火墙挡掉而不是改代码。4. 抓包与流量解析核心实现pcap 捕获、协议解析与 WebSocket 推送4.1 抓包引擎选型Pcap4J 为什么比 Jpcap 合适Java 网络流量分析课程设计里绕不开一个选型问题用 Jpcap 还是 Pcap4J。老牌方案 Jpcap 存在很多年了但项目维护基本停滞绑定的 WinPcap 版本偏旧经常在 JDK 9 以上的环境翻车。近几年的项目更倾向用 Pcap4J它基于 JNA 动态加载 libpcap/Npcap不需要编译本地代码提供了统一的 Packet 抽象解析以太网帧、IP、TCP 明显省事对 Windows、Linux、macOS 都能覆盖。这套资源的源码路径走的也是 Pcap4J libpcap 这条主流路线。对比项JpcapPcap4J维护状态基本停止持续更新本地库绑定需要手动配置JNA 自动加载包解析能力需要自己拆字节按协议类直接 get 出来JDK 兼容性老版本友好新 JDK 更稳4.2 网卡枚举与实时抓包一个能跑的起点抓包前必须知道自己机器上有哪些可用的网卡Windows 上尤其是这样网卡名长得像\Device\NPF_{0A1B2C3D-...}不枚举根本记不住import org.pcap4j.core.Pcaps; import org.pcap4j.core.PcapNetworkInterface; ListPcapNetworkInterface allDevs Pcaps.findAllDevs(); for (PcapNetworkInterface dev : allDevs) { System.out.println(dev.getName() - dev.getDescription()); }Pcaps.findAllDevs() 返回本机可用的非回环接口列表拿到名字后就能匹配常用配置.txt 里的 capture.device。如果没有输出任何网卡多半是权限或驱动问题这类坑在第 5 章单独展开。网卡选定后打开实时抓包句柄PcapNetworkInterface nif Pcaps.getDevByName(deviceName); PcapHandle handle nif.openLive(65535, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10_000); handle.setFilter(tcp or udp, BpfProgram.BpfCompileMode.OPTIMIZE); handle.loop(-1, packet - TrafficAnalyzer.onPacket(packet));三个核心参数。snaplen 65535 表示每个包最多拷贝 65535 字节对主流 MTU 来说足够PromiscuousMode 让网卡接收所有经过的数据帧而不只是发给本机的timeout 10000 毫秒控制读取超时影响的是句柄的响应灵敏性不是丢包。setFilter 用 BPF 表达式过滤掉无关协议。loop 的第一个参数 -1 是无限捕获填正数如 100 就跑 100 个包后自动停止。调试阶段建议先用 100 个包跑通链路再改成无限循环。4.3 协议解析从帧到 TCP 会话的判定Pcap4J 用起来顺手的点在这。Packet 内部已经按协议栈拆好了想判断包是不是 IPv4/TCP直接 get 对应的类实例IpV4Packet ipv4 packet.get(IpV4Packet.class); if (ipv4 null) { return; // 不是 IPv4直接跳过 } TcpPacket tcp packet.get(TcpPacket.class); if (tcp null) { return; // UDP 或 ICMP 走另一个分支 } IpV4Packet.IpV4Header ipHeader ipv4.getHeader(); TcpPacket.TcpHeader tcpHeader tcp.getHeader(); String srcKey ipHeader.getSrcAddr() : tcpHeader.getSrcPort(); String dstKey ipHeader.getDstAddr() : tcpHeader.getDstPort(); String flowKey srcKey - dstKey;get(Class) 返回 null 表示当前包不是该协议所以判空是必须的。flowKey 用“源地址:源端口-目的地址:目的端口”拼出来它就是连接表的主键。这里要提醒Pcap4J 的 getSrcPort() 返回的是对象而不是基础类型写代码时别拿它和 int 直接做 比较常见做法是调用 valueAsInt() 再比较或拼字符串。从 TcpPacket 里还能拿到 TCP flagsSYN 表示连接发起FIN 表示结束RST 表示异常中断。这些状态位是维护连接表状态机的基础。HTTP 报文是 TCP payload 的一部分想识别 HTTP 请求拿 payload 做一次简单探测以 GET/POST/PUT 开头且包含 HTTP/1.x基本可以判定是 HTTP 流量。4.4 连接跟踪与流量统计内存表怎么维护连接表是这套软件的核心状态结构。我习惯用一个 ConcurrentHashMap 存连接记录key 就是 flowKeyvalue 是一个包含包数、字节数、起始时间、最后活跃时间的对象。每个包进来只做三件事更新共享表、更新字节计数、记录最后活跃时间。每秒的定时任务扫描这张表超过 5 分钟没有新包到达的连接标记为过期从表里清掉。FlowRecord record flowMap.computeIfAbsent(flowKey, k - new FlowRecord(flowKey)); record.bytes tcpHeader.getPayloadLength(); record.packets; record.lastSeen nowMillis();computeIfAbsent 保证同一个五元组只初始化一条记录。这里有一个隐藏问题如果只往里写不往外清运行几小时后会看到内存不断增长。原因通常是清理过期连接表的定时任务没跑起来或者清理阈值设太大。连接表一定要配合定期清理否则就是一场内存事故。统计聚合用 java.util.concurrent.ScheduledExecutorService 就够了每秒跑一次扫描不需要引额外的定时任务框架这也是 Java 入门里最该养成习惯的地方周期任务用调度线程池而不是每次都 new Thread。4.5 WebSocket 实时推送把统计结果发到浏览器后端拿到统计快照后要把它发给所有在线页面。用 Java-WebSocket 实现服务端大致是创建一个 WebSocketServer覆盖 onOpen/onMessage/onClose 维护 Session 集合public class TrafficWsServer extends WebSocketServer { private final CopyOnWriteArraySetSession sessions new CopyOnWriteArraySet(); Override public void onOpen(Session session) { sessions.add(session); } Override public void onMessage(Session session, String message) { // ping 心跳、查询请求可以在这里处理 } public void broadcast(String json) { sessions.forEach(s - s.send(json)); } }广播时给每个 Session 单独 send 最直接但要注意 send 是异步的高速连续推送时不要在同一线程里疯狂调用否则 CPU 会大量消耗在线程调度上。我一般把推送周期固定到 1 秒一次把这一秒内的统计合并成一条 JSON 广播出去既满足“实时”又不把 WebSocket 拖死。浏览器端就是 new WebSocket(wss://主机:8443/traffic)onmessage 里更新图表数据。5. 常见问题与排查抓不到包、连不上、卡死翻车的几个现场这一章集中写我跑这类项目时踩过的、以及同学们最容易复现的五个坑。每条按“现象 → 原因 → 解决”展开照着排查能省不少时间。5.1 网卡列表为空权限或者驱动的问题现象执行 Pcaps.findAllDevs() 返回空列表启动日志直接报 no devices found。原因Linux 下普通用户没有原始套接字能力libpcap 拿不到网卡Windows 下装了老版 WinPcap 而不是 Npcap或者 Npcap 安装时没有勾选“兼容模式”。解决Linux 上先 sudo 跑一遍验证如果 sudo 下正常说明是权限问题给 java 进程设置 capabilitiessudo setcap cap_net_raw,cap_net_admineip $(readlink -f $(which java))Windows 上安装 Npcap 并勾选“以兼容模式安装”装完重启再试。5.2 WebSocket 连接秒断自签证书没被信任现象浏览器打开页面后WebSocket 连接建立一两秒就断开控制台报 ERR_CERT_AUTHORITY_INVALID 或 ERR_SSL_PROTOCOL_ERROR。原因certs 里的自签证书不被浏览器信任浏览器与 wss 握手时先做证书校验校验不过直接断开。后端侧可能也会收到 SSLHandshakeException。解决开发环境先在浏览器里访问一次 https://ip:8443手动放行证书Chrome 就是“高级→继续前往”之后再连接 WebSocket 就正常如果后端开了双向认证还要把客户端证书导入信任库。生产环境换受信任 CA 签发的证书这一步没有捷径。5.3 长时间运行 UI 卡死有界队列和 GC 在作怪现象监控了半小时到一小时后浏览器图表越来越卡后端 CPU 飙升最后页面无响应。原因抓包线程和解析、推送线程速度不匹配。抓包回调每秒钟进几百上千个包如果消费线程忙着做序列化和推送缓冲队列里的包越积越多内存占用上涨触发频繁 GCGC 又导致消费更慢形成恶性循环。解决给抓包到解析之间的缓冲队列设置上限比如 ArrayBlockingQueue(10000)满了直接丢弃并累加丢包计数推送周期固定为 1 秒避免高频 sendJVM 参数建议-Xms512m -Xmx1g -XX:UseG1GC。在“常用配置.txt”里加一个 queue.capacity 字段默认 10000 对课程设计场景够用。5.4 端口和长度解析错乱字节序的坑现象界面上端口显示成 40988 这种明显不对的大数或者 TCP 分段长度与 Wireshark 显示不一致。原因手写字节解析时用了主机小端序去读网络大端序的字段导致端口高低字节互换长度字段错乱。Pcap4J 的 getHeader() 已经处理了字节序但如果你为了某些自定义字段自己拼 ByteBuffer就很容易踩进去。解决所有手工解析都通过ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN)来读别用 DataInputStream.readShort() 这种默认端序去读网络包。另外TCP payload 长度应该用 IP 头声明的总长度减去 IP 头长度再减去 TCP 头长度不要自己数 payload 数组的长度。5.5 jar 包启动报 UnsatisfiedLinkErrorPcap4J 找不到本地库现象java -jar traffic_analysis-1.0.jar 启动后立刻抛 PcapNativeException 或 UnsatisfiedLinkError提示找不到 libpcap.so 或 wpcap.dll。原因Pcap4J 是通过 JNA 在运行时加载本地库本地库不在 jar 里。Linux 目标机上没装 libpcapWindows 上没装 NpcapJNA 找不到入口整个抓包模块就废了。解决Linux 先安装 libpcap 开发包Debian/Ubuntu 上执行apt install libpcap-devCentOS/RHEL 上执行yum install libpcap-develWindows 安装 Npcap。如果装完还报错把动态库所在目录加到 PATH或者通过-Djna.library.path指定目录。这门课的实验环境经常是 Windows 本机加 Linux 服务器两边都要提前装好。6. 进阶用离线 pcap 回放做回归验证让解析结果可对比6.1 为什么需要离线回放实时抓包环境不是一直可靠的现场没有流量、跨网段抓不到、抓了几小时之后不好复现。我写这个项目时最痛苦的是改协议解析逻辑没有固定“输入数据”每次都得重新抓包。后来把 Pcap4J 的离线读取拿来做回归验证一次抓包固化成文件以后改解析逻辑不再折腾真实网络。离线文件不需要 root 权限不需要选网卡测试数据可控是做解析逻辑验证最舒服的路径。6.2 离线读取的代码骨架try (PcapHandle handle Pcaps.openOffline(/path/to/test.pcap)) { Packet packet; int count 0; while ((packet handle.getNextPacket()) ! null) { TrafficAnalyzer.onPacket(packet); count; } System.out.println(replayed packets count); }openOffline 打开保存好的 pcap 文件getNextPacket 一次读一个包读到文件末尾返回 null。这样跑出来的统计结果和实时模式完全一致因为 onPacket 不关心数据来源。如果你要精确复现时间间隔Pcap4J 的 openOffline 还支持时间戳精度参数精确到微秒或纳秒做延时分析时会用到。测试数据用 tcpdump 生成最简单抓 5000 个包就够覆盖绝大多数解析场景tcpdump -i eth0 -w test.pcap tcp or udp -c 50006.3 验证三个关键数字回放之后重点看三个数处理包数、识别连接数、统计字节数。与 Wireshark 对拍是最有效的验证手段用 Wireshark 打开同一个 pcap 文件看“捕获文件属性”里的包数和总字节数应该和这边打印的一致连接数量在 Wireshark 的“统计→对话”里对一下 TCP 会话数。验证项本程序输出Wireshark 对照结论总包数50005000一致TCP 会话数231230差 1 条多半是半开连接判定差异总字节数8.2 MB8.2 MB一致如果包数一致而连接数差一两条通常是连接表清理策略和 Wireshark 的状态机判定不同比如对只有 SYN 没有后续包的半开连接统计口径不同。这个差异可以接受但要知道它存在答辩时被问到也能解释清楚。从那以后我每次改完解析器逻辑都强制自己跑一遍离线回放把三个数字导出来和上一版 diff 一下确认影响范围再去做实时抓包。看起来多花几分钟但协议解析这种黑匣子不把基准数据留好出了问题根本不知道改坏了哪里。希望这个习惯能帮到你。完整的源码、jar 包和课程设计报告书都在编号 100010394 的压缩包里下载后先按第 3 章的构建流程跑通再替换成你自己的网卡名和证书。本文还有配套的精品资源点击获取
返回列表