1.jpcap说明与安装
JAVA语言虽然在TCP/UDP传输方面给予了良好的定义,但对于网络层以下的控制,却是无能为力的。JPCAP扩展包弥补了这一点,jPcap是一个可以让java工作在链路层的类库;当然,它底层还是使用了本机API通过Jini调用,在javaAPI中得到数据。 JPCAP实际上并非一个真正去实现对数据链路层的控制,而是一个中间件,JPCAP调用wincap/libpcap,而给JAVA语言提供一个公共的接口,从而实现了平台无关性。在官方网站上声明,JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP等系统。 jPcap下载地址:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html ; 你可以从jpcap网站上直接下载它的桌面应用程序进行测试,可以全面的统计本机的网络数据流量及收发包数据。Jpcap is a Java class package that allows Java applications to capture and/or send packets to the network. Jpcap is based on libpcap/winpcap and Raw Socket API. Therefore, Jpcap is supposed to work on any OS on which libpcap/winpcap has been implemented. Currently, Jpcap has been tested on FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP. |
本项目中使用的是jPcap0.6版本,从其网站上下载Source build后,可以看到其下详细的目录结构,源码,例程及Native lib。
使用jPcap可以编写出功能完备的网络嗅测程序,本节中,我们只是使用其非常简单的一个功能:统计本机每块网卡上收发数据的总量。
特别注意:jpcap运行时依赖winCap的类库,使用前必须在机地安装winCap(http://www.winpcap.org/ )(如果是在liunx上,则请到http://www.tcpdump.org/ 下载)。本节中jPcap版本为0.6,winCap版本为4.0,运行与win32系统上。
2.jPcap小试:显示本机网络接口详情
jPcap中的API非常简单,可查看其在线文档:http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html 。当然,要灵活的使用,你必须有良好的tcp/ip协议知识基础,对常用的3个关键类,简介如下:JpcapCaptor类 这个类是jPcap中的核心对象,一个JpcapCaptor对象代表了了系统中的一个网络接口卡;通过对JpcapCaptor对象的调用,实现网络数据包的抓取和发送。它供了一系列静态方法调用如:获取网卡列表,获取某个网卡上的JpcapCaptor对象。 |
|
static NetworkInterface[]getDeviceList() | 这个静态方法调用,可以返回机器上网络接口卡对象的数组,数组中每一个NetworkInterface元素对象代表一个网络接口;一般使用jPcap所要做的第一步调用就是这个方法。 |
static JpcapCaptor openDevice(NetworkInterface interface, int snaplen, boolean promisc, int to_ms) | 取得在指定网卡上的Jpcapcator对象,Interface:上所返回的某个网卡对象Snaplen:一次性要抓取数据包的最大长度。Promisc:设置是否混杂模式。处于混杂模式将接收所有数据包,如果设置为混杂模式后调用了包过滤函数setFilter()将不起任何作用;To_ms:这个参数主要用于processPacket()方法,指定超时的时间; |
int loopPacket(int count, PacketReceiver handler) | 常用的一种模式是,通过getDeviceList()取得所有网络接口,再通过openDevice方法取得每个网络接口上的JpcapCaptor对象,就可通过这个方法抓包了。loopPacket方法中count参数表示要抓的包的数目,如果设备为-1,责表示永远抓下去---方法不会返回;第二个参数必须是实现了PacketReceiver接口的一个对象,抓到的包将调用这个PacketReceiver对象中的receivePacket(Packet packet)方法处理;所以抓包前,我们必须写一个实现了PacketReceiver接口的类。 特别注意的是:这个方法的调用会阻塞等待,如果没有抓到指定count的包、或count设为-1,这个方法都不会返回。所以,聪明的你肯定想到了,如果要抓取机器上多个卡口上的包,这个方法必须放在一个独立的线程中。 |
void breakLoop() | 即上JpcapCaptor对象上阻塞等待的方法强制终止。当调用processPacket()和loopPacket()后,再调用这个方法可以强制让processPacket()和loopPacket()停止。 |
interface PacketReceiver :数据包处理器接口定义,要处理收到的数据包,必须编写这个接口的实现类,在JpcapCaptor对象的loopPacket方法中调用. 这个接口中仅有一个方法定义: | |
Void receivePacket (Packet p) | 实现类中处理接收到的Packet对象的方法。每个Packet对象代表从热指定网络接口上抓取到的数据包。 |
NetworkInterface类该类的每一个实例代表一个网络设备,一般就是网卡。这个类只有一些数据成员,除了继承自java.lang.Object的基本方法以外,没有定义其它方法。(但我还不知它与jdk5.0以上的API中的java.net.InterfaceAddress类是否可以互换)。 | |
NetworkInterfaceAddress[]addresses | 这个接口的网络地址。设定为数组应该是考虑到有些设备同时连接多条线路,例如路由器。但我们的PC机的网卡一般只有一条线路,所以我们一般取addresses[0]就够了。 |
java.lang.String datalink_description. | 数据链路层的描述。描述所在的局域网是什么网。例如,以太网(Ethernet)、无线LAN网(wireless LAN)、令牌环网(token ring)等等。 |
java.lang.String datalink_name | datalink_name 该网络设备所对应数据链路层的名称。具体来说,例如Ethernet10M、100M、1000M等等。 |
java.lang.String description | 网卡是XXXX牌子XXXX型号之类的描述。例如我的网卡描述:Realtek RTL8169/8110 Family Gigabit Ethernet NIC |
boolean Loopback | 标志这个设备是否loopback设备。 |
byte[]mac_address | 网卡的MAC地址,6个字节。 |
java.lang.String Name | 这个设备的名称。例如我的网卡名称:\Device\NPF_{3CE5FDA5-E15D-4F87-B217-255BCB351CD5} |
jPcap的API使用很简单,如下代码示例:显示机器上的所有网络接口DispalyNetInterface.java:
import jpcap.JpcapCaptor; import jpcap.NetworkInterface; import jpcap.PacketReceiver; import jpcap.packet.Packet; /** * 使用jpcap显示网络接口数据. * @author 胡东峰 */ public class DispalyNetInterface { public static void main(String args[]){ try{ //获取本机上的网络接口对象数组 final NetworkInterface[] devices = JpcapCaptor.getDeviceList(); for(int i=0;i<devices.length;i++){ NetworkInterface nc=devices[i]; //一块卡上可能有多个地址: String address=""; for(int t=0;t<nc.addresses.length;t++){ address+="|addresses["+t+"]: "+nc.addresses[t].address.toString(); } //打印说明: System.out.println("第"+i+"个接口:"+"|name: "+nc.name +"|loopback: "+nc.loopback+"\r\naddress: "+address); } }catch(Exception ef){ ef.printStackTrace(); System.out.println("显示网络接口数据失败: "+ef); } } }
不幸的是,这段代码运行时会报如下错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no jpcap in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1030) at jpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250) at cn.netjava.cewolf.DispalyNetInterface.main(DispalyNetInterface.java:19) |
Exception in thread "main" java.lang.UnsatisfiedLinkError: E:\workspace\trafficManager\jpcap.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1751) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1676) at java.lang.Runtime.loadLibrary0(Runtime.java:823) at java.lang.System.loadLibrary(System.java:1030) at jpcap.JpcapCaptor.<clinit>(JpcapCaptor.java:250) at cn.netjava.cewolf.DispalyNetInterface.main(DispalyNetInterface.java:17) |
第0个接口:|name: \Device\NPF_GenericDialupAdapter|loopback: false address: 第1个接口:|name: \Device\NPF_{2A5FD532-45A3-4A2B-9B68-F34C14E4FD2C}|loopback: falseaddress: |addresses[0]: /220.192.159.105第2个接口:|name: \Device\NPF_{14303C1A-4DB3-4BC9-979E-34063E070CBB}|loopback: falseaddress: |addresses[0]: /192.168.1.44 |
在我的机器上,开着一块局网网卡和一块无线网卡,都显示出来了;上面的”第0个接口”就是指本机的127.0.0.1的回环地址。但不知为什么,loopback也会是false?
要注意两点:一个是jpcap.dll要在路径中;另外,在统计流量时,本机回环地址不需要统计,一般是第0个接口。接下来我们看抓取网卡上的数据包是多么简单: