简介:这是一份面向高校计算机网络课程设计场景的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-356,Web 前端 |
为什么要这样拆?因为采集层最高频的操作是每秒钟成百上千个回调,展示层的数据是每秒定期批量推送一次,两边的频率和容错策略完全不同。拆开之后,抓包线程只做一件事,推送线程只做另一件事,参数可以各自调。分析层是纯 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.port | 8443 | WebSocket 服务端口,避开 80/443 的低端口权限问题 |
| capture.device | eth0 或 \Device\NPF_{...} | 要监控的网卡名,Windows 上先枚举再填 |
| bpf.filter | "tcp or udp" | 伯克利包过滤表达式,减少无效包 |
| aggregate.interval | 1000 | 统计聚合周期,单位毫秒 |
| keystore.path | certs/server.p12 | Java 读取的密钥库路径 |
| keystore.password | 由资源文档决定 | 密钥库口令,建议放环境变量而不是明文 |
我见过不少同学把 capture.device 配错:Linux 上写 eth0 没问题,Windows 上不先枚举网卡直接凭印象填名字,大概率抛“找不到网卡”的异常。配置解析建议做成“留空时自动选择第一个非回环网卡”,首跑体验会好很多。
3. Gradle 构建与项目启动:从源码到 traffic_analysis-1.0.jar
3.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 脚本与常用配置.txt
Gradle 的 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 2>&1 &看到“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 这条主流路线。
| 对比项 | Jpcap | Pcap4J |
|---|---|---|
| 维护状态 | 基本停止 | 持续更新 |
| 本地库绑定 | 需要手动配置 | JNA 自动加载 |
| 包解析能力 | 需要自己拆字节 | 按协议类直接 get 出来 |
| JDK 兼容性 | 老版本友好 | 新 JDK 更稳 |
4.2 网卡枚举与实时抓包:一个能跑的起点
抓包前必须知道自己机器上有哪些可用的网卡,Windows 上尤其是这样,网卡名长得像\Device\NPF_{0A1B2C3D-...},不枚举根本记不住:
import org.pcap4j.core.Pcaps; import org.pcap4j.core.PcapNetworkInterface; List<PcapNetworkInterface> 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 flags:SYN 表示连接发起,FIN 表示结束,RST 表示异常中断。这些状态位是维护连接表状态机的基础。HTTP 报文是 TCP payload 的一部分,想识别 HTTP 请求,拿 payload 做一次简单探测:以 GET/POST/PUT 开头且包含 HTTP/1.x,基本可以判定是 HTTP 流量。
4.4 连接跟踪与流量统计:内存表怎么维护
连接表是这套软件的核心状态结构。我习惯用一个 ConcurrentHashMap 存连接记录,key 就是 flowKey,value 是一个包含包数、字节数、起始时间、最后活跃时间的对象。每个包进来只做三件事:更新共享表、更新字节计数、记录最后活跃时间。每秒的定时任务扫描这张表,超过 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 CopyOnWriteArraySet<Session> 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 进程设置 capabilities:
sudo setcap cap_net_raw,cap_net_admin=eip $(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 飙升,最后页面无响应。
原因:抓包线程和解析、推送线程速度不匹配。抓包回调每秒钟进几百上千个包,如果消费线程忙着做序列化和推送,缓冲队列里的包越积越多,内存占用上涨触发频繁 GC,GC 又导致消费更慢,形成恶性循环。
解决:给抓包到解析之间的缓冲队列设置上限,比如 ArrayBlockingQueue(10000),满了直接丢弃并累加丢包计数;推送周期固定为 1 秒,避免高频 send;JVM 参数建议-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 包启动报 UnsatisfiedLinkError:Pcap4J 找不到本地库
现象:java -jar traffic_analysis-1.0.jar 启动后立刻抛 PcapNativeException 或 UnsatisfiedLinkError,提示找不到 libpcap.so 或 wpcap.dll。
原因:Pcap4J 是通过 JNA 在运行时加载本地库,本地库不在 jar 里。Linux 目标机上没装 libpcap,Windows 上没装 Npcap,JNA 找不到入口,整个抓包模块就废了。
解决:Linux 先安装 libpcap 开发包,Debian/Ubuntu 上执行apt install libpcap-dev,CentOS/RHEL 上执行yum install libpcap-devel;Windows 安装 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 对照 | 结论 |
|---|---|---|---|
| 总包数 | 5000 | 5000 | 一致 |
| TCP 会话数 | 231 | 230 | 差 1 条,多半是半开连接判定差异 |
| 总字节数 | 8.2 MB | 8.2 MB | 一致 |
如果包数一致而连接数差一两条,通常是连接表清理策略和 Wireshark 的状态机判定不同,比如对只有 SYN 没有后续包的半开连接统计口径不同。这个差异可以接受,但要知道它存在,答辩时被问到也能解释清楚。
从那以后我每次改完解析器逻辑,都强制自己跑一遍离线回放,把三个数字导出来和上一版 diff 一下,确认影响范围再去做实时抓包。看起来多花几分钟,但协议解析这种黑匣子,不把基准数据留好,出了问题根本不知道改坏了哪里。希望这个习惯能帮到你。完整的源码、jar 包和课程设计报告书都在编号 100010394 的压缩包里,下载后先按第 3 章的构建流程跑通,再替换成你自己的网卡名和证书。
本文还有配套的精品资源,点击获取