Jpcap是2003年日本开发的一套能够捕获、发送网络数据包的java类库。因为核心Java API不能访问底层的网络数据,但Jpcap是一种提供在Windows或UNIX系统上进行这种访问的Java API。Jpcap不是一种纯粹的Java解决方案,它依赖本地库的使用。在Windows 或 UNIX上,你必须有必要的第三方库,分别是WinPcap或libpcap。要在java中使用Jpcap类库需要安装Jpcap的运行和开发环境。
安装(预装JAVA环境)
Windows
1、下载并安装WinPcap
2、下载Jpcap相关库,安装方法如下两张:
方法一:运行附带的安装器(JpcapSetup-0.6.exe)进行安装,注意选择JRE和SDK。
方法二:
- 拷贝 “lib\Jpcap.dll” 到 “[JRE directory]\bin” 或者 “[JRE directory]\lib\ext\x86”
- 拷贝 “lib\jpcap.jar” 到 “[JRE directory]\lib\ext”
- 如果安装SDK的话,还需要拷贝 “lib\jpcap.jar” 到 “[JDK directory]\jre\lib\ext”.
Unix
1、下载并安装libpcap
2、下载Jpcap相关库,进入到 “src/c” 目录,并且配置好Makefile
3、在终端运行make命令
如果出现错误 “structure has no member named `sa_len'”,则注释掉 “#define HAVE_SA_LEN” 在文件Jpcap_sub.h中。
4、把生成的 libjpcap.so 拷贝到 [Java directory]/jre/lib/<arch>。( <arch> is either “i386” or “sparc”)
5、把 “lib/jpcap.jar” 拷贝到 [Java directory]/jre/lib/ext.
主要步骤
1、实现PacketReceiver(接口)
该接口是用来定义一个分析被捕获数据包的方法。需要实现当前接口,当processPacket() 或loopPacket()捕捉到数据包之后被动调用接口中PacketReceiver方法。
2、获得网卡信息
可能含有多个网卡,故用数组形式存储,一个对象代表一个网卡信息
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
一个网卡可能含有多个地址,同时注意会换地址的筛选
// 获取本机上的网络接口对象数组 final NetworkInterface[] devices = JpcapCaptor.getDeviceList(); //获取本机第一个有ipv4网址且不是回环地址的网络接口 for (NetworkInterface nc : devices) { // 一块卡上可能有多个地址: if(nc.addresses.length > 0 && !nc.loopback){ for (int t = 0; t < nc.addresses.length; t++) { if(nc.addresses[t].address instanceof Inet4Address){ currentDevice = nc; //这个就是需要获取的网络接口对象,在这里可以通过它获取ip和子网掩码如nc.addresses[t].address.getHostAddress() nc.addresses[t].subnet.getAddress() return nc.addresses[t]; } } } }
3、对相关网卡进行监听
JpcapCaptor.openDevice(NetworkInterface intrface, int snaplen, boolean promisc, int to_ms);
intrface参数是监听的网卡;snaplen参数是一次捕获的最大字节数量(本例:65535);promisc参数,如果为true,接口被设置为混杂模式(本例:true);to_ms参数,设定processPacket()中的Timeout(本例:20);当指定接口不能被打开抛出IO异常。
4、捕获数据包
此时可以调用processPacket() 或loopPacket()开始监听了。这两种方式都带有两个参数:捕获的最大包数可以是-1(说明没有限制);执行PacketReceiver的一个类的实例。
int processPacket(int count,JpcapHandler handler);
int loopPacket(int count,JpcapHandler handler);
作用:连续地捕获数据包,返回捕获数据包的数量。参数count是要捕获数据包的数量,可以将其设置为-1,这样就可以持续抓包直到EOF或发生错误为止。
区别:loopPacket忽视超时。
5、分析数据包
该部分主要在PacketReceiver接口的实现类中进行体现。
代码实现:
1、主函数中
public class main { public static void main(String[] args) { // 获得网络设备列表 NetworkInterface[] devices = JpcapCaptor.getDeviceList(); NetworkInterface ni = null; String str = ""; JpcapCaptor jpcap; for (int i = 0; i < devices.length; i++) { ni = devices[i]; for (int j = 0; j < ni.addresses.length; j++) { // 一个网卡可能有多个地址,获得本机IP地址 str = ni.addresses[j].address.toString(); } // 创建卡口上的抓取对象 try { jpcap = JpcapCaptor.openDevice(ni, 65535, true, 20); // 创建对应的抓取线程并启动 jpcap.loopPacket(-1, new getPacket(str)); } catch (IOException e) { e.printStackTrace(); } } System.out.print(str); } }
2、getPacket类实现PacketReceiver
public class getPacket implements PacketReceiver { String ipAddr;// 本机ip地址数组 private int upLoadPacketSize = 0;// 上传数据包总字节数 private int downLoadPacketSize = 0;// 下载数据包总字节数 private int upLoadIPPacketSize = 0;// 上传IP数据包总字节数 private int downLoadIPPacketSize = 0;// 下载IP数据包总字节数 private int upLoadTCPPacketSize = 0; // 上传TCP数据包总字节数 private int downLoadTCPPacketSize = 0; // 下载TCP数据包总字节数 private int upLoadUDPPacketSize = 0; // 上传UDP数据包总字节数 private int downLoadUDPPacketSize = 0; // 下载UDP数据包总字节数 private static Packet currentPacket = new Packet();// 表示最新抓取的包 public int getupLoadPacketSize() { return upLoadPacketSize; } public int getdownLoadPacketSize() { return downLoadPacketSize; } public int getupLoadIPPacketSize() { return upLoadIPPacketSize; } public int getdownLoadIPPacketSize() { return downLoadIPPacketSize; } public int getupLoadTCPPacketSize() { return upLoadTCPPacketSize; } public int getdownLoadTCPPacketSize() { return downLoadTCPPacketSize; } public int getupLoadUDPPacketSize() { return upLoadUDPPacketSize; } public int getdownLoadUDPPacketSize() { return downLoadUDPPacketSize; } public getPacket() { // 空实现 } // public getPacket(String[] ipAddr, int ipcount) { // this.ipAddr = ipAddr; // this.ipcount = ipcount; // } public getPacket(String ipAddr) { this.ipAddr = ipAddr; } // 判断当前抓的包是否为空,若为空则表示断网,否则没断网 public static boolean isPacketEmpty() { if (currentPacket == null) { return true; } else { return false; } } // 获得截取的数据包 public void receivePacket(Packet p) { // currentPacket=null; if (p instanceof TCPPacket) {// 如果截取的是TCP包 TCPPacket tcp = (TCPPacket) p; if (tcp.src_ip.getHostAddress().equals(ipAddr.substring(1))) {// 如果TCP包的源IP地址等于本机ip则此包为上传包 upLoadTCPPacketSize += tcp.len; upLoadPacketSize += tcp.len; } else if (tcp.dst_ip.getHostAddress().equals( ipAddr.substring(1))) {// 如果TCP包的目的IP地址等于本机ip则此包为下载包 downLoadTCPPacketSize += tcp.len; downLoadPacketSize += tcp.len; } } if (p instanceof UDPPacket) {// 如果截取的是UDP包 UDPPacket udp = (UDPPacket) p; if (udp.src_ip.getHostAddress().equals(ipAddr.substring(1))) {// 如果UDP包的源IP地址等于本机ip则此包为上传包 upLoadUDPPacketSize += udp.len; upLoadPacketSize += udp.len; } else if (udp.dst_ip.getHostAddress().equals( ipAddr.substring(1))) {// 如果UDP包的目的IP地址等于本机ip则此包为下载包 downLoadUDPPacketSize += udp.len; downLoadPacketSize += udp.len; } } if (p instanceof IPPacket) {// 如果截取的是IP包 IPPacket ippacket = (IPPacket) p; if (ippacket.src_ip.getHostAddress().equals(ipAddr.substring(1))) {// 如果IP包的源IP地址等于本机ip则此包为上传包 upLoadIPPacketSize += ippacket.len; upLoadPacketSize += ippacket.len; } else if (ippacket.dst_ip.getHostAddress().equals( ipAddr.substring(1))) {// 如果IP包的目的IP地址等于本机ip则此包为下载包 downLoadIPPacketSize += ippacket.len; downLoadPacketSize += ippacket.len; currentPacket = p; } } System.out.println("上传数据包:" + upLoadPacketSize + "字节"); System.out.println("下载数据包:" + downLoadPacketSize + "字节"); System.out.println("IP上传数据包:" + upLoadIPPacketSize + "字节"); System.out.println("IP下载传数据包:" + downLoadIPPacketSize + "字节"); System.out.println("TCP上传数据包:" + upLoadTCPPacketSize + "字节"); System.out.println("TCP下载传数据包:" + downLoadTCPPacketSize + "字节"); System.out.println("UDP上传数据包:" + upLoadUDPPacketSize + "字节"); System.out.println("UDP下载传数据包:" + downLoadUDPPacketSize + "字节"); // System.out.println(System.currentTimeMillis()); } }
这几天就在找这方面的内容,学习了 谢谢
有运行结果的截图吗
没有截图,因为当时没有做界面 只是System.out输出了,结果都正确,文本显示的