一、前言

在​​WinPcap打开适配器捕获数据​​​一文中,已经讲解了如何打开适配器并捕获数据包,但是是使用​​pcap_loop()​​函数,通过回调方法进行捕获的。

本文将用​​pcap_next_ex()​​​函数代替​​pcap_loop()​​​函数,​​pcap_loop()​​函数是基于回调的原理来进行数据捕获,这是一种精妙的方法,并且在某些场合,它是一种很好的旋转。然而,处理回调的时候并不实用(它会增加程序的复杂度,特别是在拥有多线程的C++程序中)。

可以通过直接调用​​pcap_next_ex()​​​函数来获得一个数据包(只有当编程人员使用了​​pcap_next_ex()​​​函数才能收到数据包)。这个函数的参数和捕获回调函数的参数是一样的(包含一个网络适配器的描述符和两个可以初始化和返回给用户的指针【一个指向​​pcap_pkthdr​​结构体,另一个指向数据包数据的缓冲】)。


二、代码详解

#include "mainwindow.h"
#include <QApplication>
#include <QDebug>

#define
#include "pcap.h"

#ifndef
#include <sys/socket.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif


int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();


pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t* adhandle;
int res;
char errbuf[PCAP_ERRBUF_SIZE];
struct tm* ltime;
char timestr[16];
struct pcap_pkthdr* header;
const u_char* pkt_data;
time_t local_tv_sec;

//获取本机适配器列表
if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&alldevs, errbuf) == -1)
{
qDebug() << "Error in pcap_findalldevs_ex: " <<errbuf;
exit(1);
}

//打印适配器列表
for(d = alldevs; d; d = d->next)
{
//设备名(Name)
qDebug()<<"Name: "<<d->name;
++i;
//设备描述(Description)
if (d->description) {
qDebug()<<"Description: "<<d->description;
}else {
qDebug()<<"No description available";
}

qDebug()<<"====================================================================";
}

if(i==0) {
qDebug()<<"No interfaces found! Make sure WinPcap is installed.";
return -1;
}

qDebug()<<QString("Enter the interface number (1-%1): ").arg(i);
//scanf("%d",&inum);
inum = 4;

qDebug()<<"inum: "<<inum;

if(inum < 1 || inum > i){
qDebug()<<"Interface number out of range.";
//释放适配器列表
pcap_freealldevs(alldevs);
return -1;
}

//跳转到选中的适配器
for(d=alldevs,i=0; i<inum-1; d=d->next,i++);

//打开设备
if((adhandle = pcap_open(d->name, //设备名
65536, //65535包证能捕获到不同数据链路层上的每个数据包的全部内容
PCAP_OPENFLAG_PROMISCUOUS, //混杂模式
1000, //读取超时时间
NULL, //远程机器验证
errbuf //错误缓冲池
)) == NULL) {
qDebug()<<"Unable to open the adapter."<<QString("%1 is not support by WinPcap").arg(d->name);

//释放适配器列表
pcap_freealldevs(alldevs);
return -1;
}

qDebug()<<QString("Listening on %1...").arg(d->description);

//释放适配器列表
pcap_freealldevs(alldevs);

//开始捕获
while((res = pcap_next_ex(adhandle,&header,&pkt_data)) >= 0) {
if(res == 0) {
//超时时间到
continue;
}

//将时间戳转换成可识别的格式
local_tv_sec = header->ts.tv_sec;
ltime = localtime(&local_tv_sec);
strftime(timestr,sizeof timestr, "%H:%M:%S",ltime);

qDebug()<<QString("%1,%2,%3").arg(timestr).arg(header->ts.tv_usec).arg(header->len);
}

if(res == -1) {
qDebug()<<"Error reading the packets: "<<pcap_geterr(adhandle);
return -1;
}

return a.exec();
}

运行结果如下

WinPcap不用回调方法捕获数据包_数据

  • 为什么要用​​pcap_next_ex()​​​代替​​pcap_next()​​​呢? 因为​​pcap_next()​​​有一些不好的地方,首先,它效率低下,尽管它隐藏了回调的方式,但它依然依赖于函数​​pcap_dispatch()​​;其次,它不能检测到文件末尾这个状态(EOF),因此,如果数据包是从文件读取来的,那么它就不那么有用了。
  • 此外,​​pcap_next_ex()​​在成功、超时、出错或EOF的情况下,会返回不同的值。