浏览 5514 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
||
---|---|---|
作者 | 正文 | |
发表时间:2008-02-28
如果上面的例子你理解了,那么统计流量数据将是一件非常简单的事;我们的需求就是怎么将这个小模块设计得更加紧凑以便其它对象调用。 流量采集器对外提供调用的类是Tcpdump.java,这个类要根据网卡个类启动对应的后台线程抓取统计数据---也就是说这个启只能启动一次所有线程,无论其它对象如何调用它。因此我们将Tcpdump.java设计为单实例类;Tcpdump类所要启动的后台线程是LoopPacketThread.java,这个类是实现了Thread的线程,它的run方法中将运行对应的网卡数据统计调用; Tcpdump类中有一个Map,用以存放对应网卡上采集的数据,采集线程会将统计到的数据根据其对应的网卡名字放入到Map中保存。
首先我们看LoopPacketThread.java类的实现: /** *统计某一个网卡上流线的线程 * @author www.NetJava.cn */ class LoopPacketThread extends Thread{ private JpcapCaptor jpcap ; private String ipAdd; /** * 构造器 * @param jpcap:cap对象 * @param ipAdd:网络地址名字 */ public LoopPacketThread(JpcapCaptor jpcap,String ipAdd){ this.jpcap=jpcap; this.ipAdd=ipAdd; } public void run(){ //-1表示永远抓取 this.jpcap.loopPacket(-1, new DumpPacket(this.ipAdd)); } } 这个类很简单,关键是它的run方法中,在loopPacket时传入了DumpPacket对象,DumpPacket.java是实现了PacketReceiver接口的类,在其中对网卡数据进行统计,并将统计结果放入缓存: /** * 抓包监听器,实现PacketReceiver中的方法,当数据包到达时计数. * @author www.NetJava.cn */ class DumpPacket implements PacketReceiver { private String ipAdd; DumpPacket(String ipAdd){ this.ipAdd=ipAdd; } //实现包统计 public void receivePacket(Packet packet) { //将数据加入缓存表中待图片生成servlet提取 Tcpdump.ins().putNetValue(ipAdd, packet.len); System.out.println(currentTime()+": "+ipAdd+" 收到长度为:*"+ packet.len); System.out.println(packet); } /** *日志时间信息 * @return:日志内容时间 */ private static String currentTime(){ Date d = new Date(); SimpleDateFormat kk=new SimpleDateFormat("mm:ss"); String strTime=kk.format(d); return strTime; } } OK,接下来就是关键的Tcpdump类实现了:
/** * 1.使用jpcap抓取网络流量的主类, * 2.这个类要根据网卡个数,启动线程分别抓取各个网卡上的流量入表中 * 3.生成图表的对象从流量表中取出数据 * 4.这个类设计为单实例,在第一次调用时启动抓数据线程; * 5.目前没有设计停止抓取机制.... * @author www.NetJava.cn */ public class Tcpdump { /** * 单实例调用:其它对象调用这个类的方法时,必须通过这个方法 * 这样,保证了流量统计线程的启动,且只启动了一次 * */ public synchronized static Tcpdump ins(){ if(null==tcpdump){ tcpdump=new Tcpdump(); tcpdump.init(); } return tcpdump; } /**生成报表的Servlet调用用于生成图表中数据*/ public Map<String, Integer> getNameTrafficMap(){ return nameTrafficMap; } /** * 根据网卡个数,启动统计线程 * 注意:本地地址,即127.0.0.1上的不统计 */ private void init() { try{ //获取本机上的网络接口对象 final NetworkInterface[] devices = JpcapCaptor.getDeviceList(); for(int i=0;i<devices.length;i++){ NetworkInterface nc=devices[i]; //大与零则为有效网卡,不抓本机地址. if(nc.addresses.length>0){ //一个网卡可能有多个地址,只取第一个地址 String addr=nc.addresses[0].address.toString(); // 创建某个卡口上的抓取对象, JpcapCaptor jpcap = JpcapCaptor.openDevice(nc, 2000, true, 20); //创建对应的抓取线程并启动 LoopPacketThread lt=new LoopPacketThread(jpcap,addr); lt.start(); System.out.println( addr+"上的采集线程己启动************"); } } }catch(Exception ef){ ef.printStackTrace(); System.out.println("start caputerThread error ! ! ! !"+ef); } } /**IP和抓到包的长度放入hash表中,用表中长度加入放入的长度*/ void putNetValue(String name,int value){ if(nameTrafficMap.containsKey(name)){ value=nameTrafficMap.get(name)+value; nameTrafficMap.put(name, value); }else{ nameTrafficMap.put(name, value); } } private Tcpdump(){} /**存入某个地址名字和流量统计值*/ private Map<String, Integer> nameTrafficMap=new java.util.HashMap(); //单实例 private static Tcpdump tcpdump=null; } 这个类除了要注意单实例设计模式个,关键的一个方法是:
/**IP和抓到包的长度放入hash表中,用表中长度加入放入的长度*/ void putNetValue(String name,int value){ if(nameTrafficMap.containsKey(name)){ value=nameTrafficMap.get(name)+value; nameTrafficMap.put(name, value); }else{ nameTrafficMap.put(name, value); } }
这个方法保证了采集到的数据在内存中的连续性,生成图片的Servlet从这个map中每取一次对应网卡的数据,就将其历史统计值清零。这三个类都理解完毕后,我们可以写一个方法来测试它是否正常:
public static void main(String args[]){ //启动统计线程 Tcpdump.ins(); //模拟数据提取线程 new Thread( new Runnable(){ public void run(){ while(true){ try{ Thread.sleep(3000); }catch(Exception ef){} System.out.println("***********统计到的数据************"); System.out.println(Tcpdump.ins().getNameTrafficMap());; } } } ).start(); } 这个方法在启动了采集线程后,接着启动一个线程每隔3秒打印一次采集到的数据,在我的机器上,开两块网卡,打印出的结果如下:
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
||
返回顶楼 | ||
发表时间:2008-06-18
你好 我用jpcap来获取1秒或者2秒的总流量 然后算出速度。但是这个速度和测试的时候的传输速度并不一致 而且相差很大 请问是什么问题 谢谢
我用的代码就是你的这篇文章的代码改了一点测试的。谢谢 |
||
返回顶楼 | ||