一个简单的网络数据包捕获工具的实现
一、网络流量监测的原理
在共享式以太网中,所有的通讯都是广播的,也就是说通常在同一网段的所有网络接口都可以访问在物理媒体上传输的所有数据,使用ARP和RARP协议进行相互转换。在正常的情况下,一个网络接口应该只响应两种数据帧:一是与自己硬件地址相匹配的数据帧和发向所有机器的广播数据帧。二是在一个实际的系统中,数据的收发由网卡来完成。每个以太网卡拥有一个全球难一的以太网地址。以太网地址是一个48位的二进制数。在以太网卡中内建有一个数据报过滤器。该数据报过滤器的作用是保留以本身网卡的MAC地址为通讯目的的数据报和广播数据报,丢弃所有其它无关的数据报,以免除CPU对无关的数据报作无谓的处理。这是以太网卡在一般情况下的工作方式。在这种方式下,以太网卡只将接收到的数据报中与本机有关部分向上传递。然而数据报过滤器是可以通过编程禁用的。禁用数据报过滤器后,网卡将把接收到的所有的数据报向上传递,上一层的软件因此可以监听以太网中其它计算机之间的通讯,称这种工作模式为“混杂模式”。通过将网卡设为“混杂模式”可以监测到网络上的数据流。
二、 JPcap库介绍及安装
在这里我们主要使用JPcap库进行构建我们的网络流量监测工具。
Jpcap是2003年日本开发的一套能够捕获、发送网络数据包的java类库。因为核心Java API不能访问底层的网络数据,但Jpcap是一种提供在Windows或UNIX系统上进行这种访问的Java API。Jpcap不是一种纯粹的Java解决方案,它依赖本地库的使用。在Windows 或 UNIX上,你必须有必要的第三方库,分别是WinPcap或libpcap。要在java中使用Jpcap类库需要安装Jpcap的运行和开发环境。
- 开发环境配置:
(1)电脑必须先安装好WinPcap和JPcap的安装文件;如果你的电脑已经安装过wireshark软件则无需安装WinPcap,仅安装JPcap即可,安装过程注意JPcap安装目录及dll文件所在的目录,稍后会用到该路径。
(2)创建自己的工程
(3)如果缺少相关开发包,请按照如下操作:复制“Jpcap.dll”到“[JRE directory]\bin”(源文件在C:\WINDOWS\system32或者C:\Windows\SysWOW64下,如果该文件夹下没有则从下载的文件中先复制到C:\WINDOWS\system32下,请注意如果是64系统则将jpcap_x64.dll修改为jpcap.dll文件后再复制)。复制“jpcap.jar”到“[JRE directory]\lib\ext”(源文件在C:\WINDOWS\Sun\Java\lib\ext下)。 - 在这里我已经把软件包放到下面的链接了,大家可以自行获取:
链接:https://pan.baidu.com/s/1_vQWOkxCMVJxEMWIsb4Gdg?pwd=cu8y
提取码:cu8y
–来自百度网盘超级会员V3的分享
三、JPcap重要类及方法介绍
(1)获取网络设备的方法getDeviceList(),调用方法如下:NetworkInterface[] devices = JpcapCaptor.getDeviceList();得到的网络设备结果保存在名为devices的数组中。
(2)打开指定的网卡
JpcapCaptor captor = JpcapCaptor.openDevice(devices[1], 65535, false, 200);使用方法openDevice()可以打开指定的网卡。
(3)设置过滤器,这样可以捕获指定的类型的数据包,调用方法如下:
captor.setFilter(“tcp”, true);该处捕获的是tcp类型的数据包。
(4)数据包处理方法,captor处理数据包有两种方法,第一种是processPacket,第二种是loopPacket,使用上述两种方法进行数据包处理时需要编写回调函数,实现PacketReceiver下的receivePacket方法;captor.loopPacket(-1, new dumpPacket());
四、代码演示
完成网络数据包,IP的分类捕获,并统计每类数据包的所占比例统计的数量和每一个IP的数量
import jpcap.*;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class DumpPacket implements PacketReceiver {
private Map<String, Integer> packetTypeCount = new HashMap<>();
private Map<String, Integer> ipPacketCount = new HashMap<>();
private static final int MAX_PACKET_COUNT = 20;
@Override
public void receivePacket(Packet packet) {
// 打印数据包
System.out.println(packet);
// 统计数据包类型
String packetType = packet.getClass().getSimpleName();
// 判断每个包类型的总数量是否超过20
if (packetTypeCount.getOrDefault(packetType, 0) < MAX_PACKET_COUNT) {
packetTypeCount.put(packetType, packetTypeCount.getOrDefault(packetType, 0) + 1);
// 进一步统计 udp、tcp、icmp、arp 的数量
if (packetType.equals("UDP")) {
// 统计 UDP 包数量
System.out.println("UDP: " + packet);
} else if (packetType.equals("TCP")) {
// 统计 TCP 包数量
System.out.println("TCP: " + packet);
} else if (packetType.equals("ICMP")) {
// 统计 ICMP 包数量
System.out.println("ICMP: " + packet);
} else if (packetType.equals("ARP")) {
// 统计 ARP 包数量
System.out.println("ARP: " + packet);
}
// 统计每个IP发送数据包的数量
if (packet instanceof IPPacket) {
String srcIP = ((IPPacket) packet).src_ip.toString();
ipPacketCount.put(srcIP, ipPacketCount.getOrDefault(srcIP, 0) + 1);
}
}
}
public static void main(String[] args) throws IOException {
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
// 选择网卡
JpcapCaptor captor = JpcapCaptor.openDevice(devices[4], 65535, false, 200);
// 设置包过滤器,仅捕捉tcp, udp, icmp, arp数据包
captor.setFilter("tcp or udp or icmp or arp", true);
DumpPacket dumpPacketHandler = new DumpPacket();
// 使用回调方法处理数据包
captor.loopPacket(100, dumpPacketHandler);
// 关闭捕获器
captor.close();
// 输出统计结果到文件
dumpPacketHandler.writeStatisticsToFile("C:\\Users\\ye'ye'y\\Desktop\\JPcap\\file.txt");
}
private void writeStatisticsToFile(String filename) {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8)) {
// 写入数据包类型统计
writer.write('\ufeff');
writer.write("packet total:\n");
for (Map.Entry<String, Integer> entry : packetTypeCount.entrySet()) {
writer.write(entry.getKey() + ": " + entry.getValue() + "\n");
}
// 写入每个IP发送数据包的数量
writer.write("\nIP packet :\n");
for (Map.Entry<String, Integer> entry : ipPacketCount.entrySet()) {
writer.write(entry.getKey() + ": " + entry.getValue() + "\n");
}
System.out.println("success.");
} catch (IOException e) {
System.err.println("error: " + e.getMessage());
e.printStackTrace();
}
}
}
运行结构演示
抓取的数据包类型(共统计4种数据包)如下:
- IP及数据包统计
- 运行截图