i3geek.com
闫庚哲的个人博客

基于Jpcap的局域网流量监控JAVA的实现

Jpcap2003年日本开发的一套能够捕获、发送网络数据包的java类库。因为核心Java API不能访问底层的网络数据,但Jpcap是一种提供在WindowsUNIX系统上进行这种访问的Java APIJpcap不是一种纯粹的Java解决方案,它依赖本地库的使用。在WindowsUNIX上,你必须有必要的第三方库,分别是WinPcaplibpcap。要在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());
	}
}

GitHub:https://github.com/yangengzhe/jpcap_monitor

赞(0)
未经允许不得转载:爱上极客 » 基于Jpcap的局域网流量监控JAVA的实现
分享到: 更多 (0)

评论 3

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    这几天就在找这方面的内容,学习了 谢谢

    远望之光3年前 (2016-01-02)回复
  2. #2

    有运行结果的截图吗

    1237个月前 (12-13)回复
    • 没有截图,因为当时没有做界面 只是System.out输出了,结果都正确,文本显示的

      yan7个月前 (12-13)回复