WinPcap不用回调方法捕获数据包
原创
©著作权归作者所有:来自51CTO博客作者贝勒里恩的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、前言
在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();
}
运行结果如下:

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