1. Jpcap
Jpcap是一个基于Java主要用来捕获网络数据包并进行处理的中间件,通过调用外部动态链接库(Windows下.dll, Linux下 .so)的形式弥补了Java不能处理数据链路层的缺陷。
2. 环境搭建
在http://jpcap.sourceforge.net/下载最新 Jpcap 打包资源
Window jpcap-x.xx.xx-win32.zip
Linux jpcap-x.xx.xx.tar.gz
(x.xx.xx为版本号,具体参考官网)
以下环境在Window系统上搭建
1)把资源包下 lib/jpcab.dll文件拷贝到当前编译环境的jre/bin目录下。
PS:使用 myEclipse作为IDE的同学如果没有设置本地JDK路径会默认在myEclipse安装的同级路径下生成AppData文件夹,以下是我的当前 jre 路径
G:\AppData\Local\Genuitec\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\jre
2)资源包下 jars/ net.sourceforge.jpcap-x.xx.xx.jar添加到项目的Reference Libraries里
或者将路径添加到classPath里。Jars/javadoc_net.sourceforge.jpcap-x.xx.xx.jar为Jpcap的API开发文档。
3. 案例
以下是Jpcap官方给出的第一个例子
import net.sourceforge.jpcap.capture.*;
import net.sourceforge.jpcap.net.*;
public classExample1
{
privatestaticfinal intINFINITE = -1;
privatestaticfinal intPACKET_COUNT = 10;
//BPF filter for capturing any packet
privatestaticfinal StringFILTER = "";
privatePacket Capturem_pcap;
private Stringm_device;
publicExample1()throws Exception {
//Step 1: Instantiate Capturing Engine
m_pcap =newPacketCapture();
//Step 2: Check for devices
// 此处官方的例子给出的是m_device = m_pcap.findDevice();经过测试,得到两个结果:
// 1)findDevice()方法取到的device的名字额外包括了适配器具体的名字
// 以下是我输出的结果:
// \Device\NPF_{EB07CCEA-BFDC-4438-B73D-AB507CFDF470}
// BroadcomNetLink (TM) Gigabit Ethernet Driver
// 红字部分才是 device需要的名字,多出来适配器的名字后,在接下来用open()调用动态链
// 接库的时候会报CaptureDeviceOpenException:Erroropening adapter异常
// 所以我用subString取了前面有效的部分正好是50个字符,具体的PC也许需要具体测试。
// 2)由于我的本机有包括虚拟网卡在内的共5个网卡适配器,findDevice()默认只取了第一个适
// 配器,并不是那个连着外网的适配器,所以抓不到何与外网交互产生的数据包,于是我用
// PacketCapture.lookupDevices()取得了所有可用的device,然后挨个测试,确定了
// 第4个device就是那个产生多数据包的适配器,同样,这个参数也需要具体测试。
m_device = PacketCapture.lookupDevices()[4].substring(0, 50);
//Step 3: Open Device for Capturing(requires root)
m_pcap.open(m_device,false);
//Step 4: Add a BPF Filter (seetcpdumpdocumentation)
m_pcap.setFilter(FILTER,true);
//Step 5: Register a Listener for RawPackets
m_pcap.addRawPacketListener(newRawPacketHandler());
// Step 6: Capture Data (max. PACKET_COUNT packets)
// 这里的参数表示具体指定捕获多少个包,设为-1时将会一直捕获直到报错或程序退出
m_pcap.capture(PACKET_COUNT);
}
publicstaticvoid main(String[] args) {
try {
Example1 example = new Example1();
} catch(Exceptione) {
e.printStackTrace();
System.exit(1);
}
}
}
class RawPacketHandlerimplementsRawPacketListener
{
privatestaticintm_counter = 0;
publicvoidrawPacketArrived(RawPacket data) {
m_counter++;
System.out.println("Receivedpacket (" +m_counter +")");
}
}
之后我重新写了这个捕获包的监听器为了只抓TCP的包
先把 Step5替换成
m_pcap.addPacketListener(newTcpPacketHandler());
接下来改了原来那个RawPacketHandler类
class TcpPacketHandlerimplementsPacketListener {
@Override
publicvoidpacketArrived(Packet packet) {
try {
//only handle TCP packets
if(packetinstanceofTCPPacket) {
TCPPacket tcpPacket = (TCPPacket) packet;
byte[] data= tcpPacket.getTCPData();
StringsrcHost = tcpPacket.getSourceAddress();
StringdstHost = tcpPacket.getDestinationAddress();
StringisoData = new String(data,"ISO-8859-1");
System.out.println(srcHost+" -> " + dstHost +":" + isoData);
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
在我运行后发现一个TCP包都捕获不到。
于是我用System.out.println(packet.getClass().getName());打印了所有捕获的
包的类型
结果如下图:
从中发现,捕获到最多的包是EthernetPackage,然后少数的IPPackage和ARPPackage
研究猜测了一种可能,就是网络层的许多包(包括TCP/UDP)在以太网上传输的时候被封装成了以太包。
具体得到其中TCPPacket的办法还在寻找,有知道的请指教一下,拜谢。