Skip to content

Latest commit

 

History

History
136 lines (104 loc) · 4.98 KB

6-GetNextPacketEx.md

File metadata and controls

136 lines (104 loc) · 4.98 KB

介绍

从本文开始将对 Pcap4J(下文简称为 p4)提供的样例代码进行注释讲解,期间还包括了对 Pcap 原理的解读


每篇文章讲解一个样例,目录如下: 目录


GetNextPacketEx

原理

这里介绍一下理解本篇文章的关键函数:dispatch 函数

  • dispatch 函数有两种重载形式,参数与 loop 函数一样
  • dispatch 和 loop 非常像,但是有本质上的不同:
    • dispatch 根据超时时间是否达到来决定是否返回,而 loop 则根据抓包数目是否达到来返回
    • loop 由数据包捕获驱动直接调用,用户无法直接控制 loop(意思就是 loop 函数太局限了)正因为如此使得 dispatch 适合应用于复杂程序
  • dispatch 是 getNextPacket 函数的基础,相当于 packetCount = 1 的 dispatch 函数,所以 getNextPacket 函数的效率比较低(因为它要等待超时)

综上,出现了一种更好的选择:GetNextPacketEx 函数,无需回调的抓包方法,这是因为

  • 回调的方法有时候并不实用,看似精妙,但是即增加了程序的复杂度还影响程序的可读性,也就是花里胡哨的意思
  • dispatch 和 loop 都是回调的方式,所以我们不选择
  • getNextPacket 基于 dispatch,只是隐藏了回调,而且效率低下
  • 最关键的是,相比于 getNextPacket ,Ex 版本增加了更多的异常处理,而这些异常是经常发生的

步骤

  • 初始化,打开、读取网卡,设置过滤器规则
  • 处理、编译过滤规则
  • 捕获数据包,及进行其他操作
  • 捕获完毕,关闭网卡

实现

package org.pcap4j.sample;

import java.io.EOFException;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.pcap4j.core.BpfProgram.BpfCompileMode;
import org.pcap4j.core.NotOpenException;
import org.pcap4j.core.PcapHandle;
import org.pcap4j.core.PcapNativeException;
import org.pcap4j.core.PcapNetworkInterface;
import org.pcap4j.core.PcapNetworkInterface.PromiscuousMode;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.TcpPacket;
import org.pcap4j.packet.namednumber.HttpStatusCode;
import org.pcap4j.util.NifSelector;

@SuppressWarnings("javadoc")
public class GetNextPacketEx {

  private static final String COUNT_KEY = GetNextPacketEx.class.getName() + ".count";
  private static final int COUNT = Integer.getInteger(COUNT_KEY, 5);

  private static final String READ_TIMEOUT_KEY = GetNextPacketEx.class.getName() + ".readTimeout";
  private static final int READ_TIMEOUT = Integer.getInteger(READ_TIMEOUT_KEY, 10); // [ms]

  private static final String SNAPLEN_KEY = GetNextPacketEx.class.getName() + ".snaplen";
  private static final int SNAPLEN = Integer.getInteger(SNAPLEN_KEY, 65536); // [bytes]

  private GetNextPacketEx() {}

  public static void main(String[] args) throws PcapNativeException, NotOpenException {
    String filter = args.length != 0 ? args[0] : "";

    System.out.println(COUNT_KEY + ": " + COUNT);
    System.out.println(READ_TIMEOUT_KEY + ": " + READ_TIMEOUT);
    System.out.println(SNAPLEN_KEY + ": " + SNAPLEN);
    System.out.println("\n");

    PcapNetworkInterface nif;
    try {
      nif = new NifSelector().selectNetworkInterface();
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }

    if (nif == null) {
      return;
    }

    System.out.println(nif.getName() + "(" + nif.getDescription() + ")");

    try (PcapHandle handle = nif.openLive(SNAPLEN, PromiscuousMode.PROMISCUOUS, READ_TIMEOUT)) {
      handle.setFilter(filter, BpfCompileMode.OPTIMIZE);

      int num = 0;
      while (true) {
        try {
          // 此代码的重点在这里:
          /*
          Ex 顾名思义, 即扩展版, 扩展在哪里呢?
          1. Ex 版本可以捕获更多的异常, 而且这些异常是经常出现的, 比如: 超时, 出错, EOF, 在不同情况下,Ex 版本会返回不同的值, 当然这些我们不必担心
             而非 Ex 版本只可以捕获网卡打开失败的异常, 这在应用场景下是不可靠的
          2. 非 Ex 版本的效率低下,尽管它也隐藏了回调的方式,但依然依赖于函数 dispatch (参数 packetCount 为 1)
          3. 非 Ex 版本不能检测到文件末尾这个状态(EOF),因此,如果数据包是从文件读取来的,必须使用 Ex 版本 (见 ReadPacket 样例)。
           */
          Packet packet = handle.getNextPacketEx();
          System.out.println(packet);
          num++;
          if (num >= COUNT) {
            break;
          }
        } catch (TimeoutException e) {
        } catch (EOFException e) {
          e.printStackTrace();
        }
      }
    }
  }
}

总结

自此,我们已经认识了所有捕获数据包的方法,其中最优的是 GetNextPacketEx