背景
在网络问题排查、协议分析或回放测试中,我们经常需要从 pcap/cap 文件中提取报文数据。
但很多 Java 方案会遇到这些问题:
- ❌
pcap4j / jNetPcap报wpcap.dll not found - ❌ 强依赖 Npcap / WinPcap(部署复杂)
- ❌ 返回的是整包(Ethernet/IP/UDP 头全在),而不是 Wireshark 里的 UDP payload
- ❌ 只想要
byte[],却被迫引入一堆协议对象
目标很明确:
用 Java 直接读取
.pcap文件,
返回List<byte[]>,
每个byte[]= Wireshark 中看到的 UDP Payload。
为什么不用 pcap4j / jNetPcap?
1️⃣ DLL 问题
pcap4j、jNetPcap本质是 抓包库- 即使只读文件,也可能触发本地库加载
- Windows 下必然遇到:
Native library (win32-x86-64/wpcap.dll) not found
2️⃣ 过度抽象
- 返回的是
Packet / IpPacket / UdpPacket - 你最终还是要
.getPayload().getRawData() - 却承担了巨大的依赖成本
👉 如果你只想离线分析 pcap 文件,完全没必要。
正确思路:直接解析 pcap 文件结构
pcap 文件结构(libpcap)
+-------------------+ | Global Header | 24 bytes +-------------------+ | Packet Header | 16 bytes +-------------------+ | Packet Data | incl_len bytes +-------------------+ | Packet Header | +-------------------+ | Packet Data | +-------------------+
关键点:
- 时间戳、抓包长度都在 Packet Header
- 真正的报文内容在 Packet Data
- Packet Data = Ethernet + IP + UDP + Payload
如何得到 Wireshark 里的 UDP Payload?
需要手动完成以下步骤:
- 跳过 pcap 头
- 解析 Ethernet Header
- 处理 VLAN Tag(可选)
- 解析 IPv4 / IPv6 Header
- 判断是否是 UDP(protocol = 17)
- 跳过 UDP Header(8 字节)
- 剩余部分 = UDP Payload
这正是 Wireshark 在做的事情。
最终实现:返回 List<byte[]>(UDP Payload)
方法签名
public static List<byte[]> readUdpPayloads(String filePath)
特点
- ✅ 纯 Java
- ✅ 不依赖任何 DLL
- ✅ 不依赖第三方库
- ✅ 返回值和 Wireshark 完全一致
- ✅ 支持:
- Ethernet
- VLAN(802.1Q / Q-in-Q)
- IPv4
- IPv6(含常见扩展头)
完整代码
⚠️ 仅支持 pcap(libpcap)
若是pcapng,请用 Wireshark / tshark 转换
public static List<byte[]> readUdpPayloads(String filePath) throws IOException {
List<byte[]> payloads = new ArrayList<>();
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(filePath)))) {
byte[] globalHeader = new byte[24];
dis.readFully(globalHeader);
boolean bigEndian;
int magic = ByteBuffer.wrap(globalHeader, 0, 4)
.order(ByteOrder.BIG_ENDIAN).getInt();
if (magic == 0xa1b2c3d4 || magic == 0xa1b23c4d) {
bigEndian = true;
} else if (Integer.reverseBytes(magic) == 0xa1b2c3d4
|| Integer.reverseBytes(magic) == 0xa1b23c4d) {
bigEndian = false;
} else {
throw new IOException("Not a pcap file");
}
int linkType = readInt(globalHeader, 20, bigEndian);
byte[] pktHeader = new byte[16];
while (true) {
try {
dis.readFully(pktHeader);
} catch (EOFException e) {
break;
}
int inclLen = readInt(pktHeader, 8, bigEndian);
byte[] packet = new byte[inclLen];
dis.readFully(packet);
byte[] payload = extractUdpPayload(packet, linkType);
if (payload != null) {
payloads.add(payload);
}
}
}
return payloads;
}
(extractUdpPayload 内部实现略,为 Ethernet + IPv4/IPv6 + UDP 解析)
使用效果
Wireshark 中
User Datagram Protocol
Source Port: 5060
Destination Port: 5060
Length: 248
Checksum: 0x1234
[UDP Payload]
01 00 00 00 7f 45 4c 46 ...
Java 中
List<byte[]> payloads = readUdpPayloads("test.pcap");
byte[] udpPayload = payloads.get(0);
// udpPayload 内容 == Wireshark 里 UDP Payload
pcap vs pcapng
| 格式 | 支持 |
|---|---|
| pcap | ✅ |
| pcapng | ❌(需转换) |
转换方式(推荐)
tshark -F pcap -r input.pcapng -w output.pcap
总结
什么时候用这种方案?
- ✔ 只做 离线分析
- ✔ 只关心 UDP payload
- ✔ 不想引入 JNI / DLL
- ✔ 希望结果和 Wireshark 100%一致
什么时候不适合?
- ❌ 实时抓包
- ❌ 需要复杂协议解析(HTTP / TLS)
