该系列文章是依据本人平时对网络分析技术的学习,归纳总结,所做的学习笔记。如有错误或待改善之处,请留下您宝贵的意见或建议。
这节的内容与上一节类似,所不同的是这一节捕获数据包使用的是pacp_next_ex()函数,而不是上一节中的pcap_loop()函数,相关的数据结构和函数介绍请见上一节。
pcap_loop()函数通过回调的方式进行数据捕获,这是一种精妙的,并且在很多场合很合适的方法。然而回调的方法也有其局限性:1.它会增加程序的复杂性,特别是在拥有多线程的C++程序中。2.它效率低下,尽管它隐藏了回调的方式,但它依然依赖于函数pcap_dispatch()。3.它不能检测到文件末尾这个状态(EOF),因此,如果数据包是从文件读取来的,那么它就不那么有用了。
可以通过直接调用pcap_next_ex()函数来获得一个数据包--只有当编程人员使用了pcap_next_ex()函数才能收到数据包。pcap_next_ex()在成功,超时,出错或EOF的情况下,会返回不同的值。
在下面的程序中,我们会再次用到上一讲中的有关回调方面的代码,只是我们将它放入了main()函数,之后调用pcap_next_ex()函数。
相关函数描述:
1. pcap_next_ex()函数,用于从一个网络适配器设备或从一个脱机文件中读取一个数据包,替代pcap_next()函数,其原型如下:
int pcap_next_ex(pcap_t *p, struct pcap_pkthdr**pkt_header, const u_char **pkt_data);
这个函数的参数和捕获回调函数的参数是一样的--它包含一个网络适配器的描述符和两个可以初始化和返回给用户的指针 (一个指向 pcap_pkthdr结构体,另一个指向数据报数据的缓冲),详见上一节中的pcap_loop()函数详解。
详细代码:
#define WIN32
#define HAVE_REMOTE
#include <stdio.h>
#include "pcap.h"
#include <winsock.h>
int main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
/* 获取本机网络设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
/* 打印网络设备列表 */
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}
if(i==0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}
/*选择网络设备接口*/
printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);
if(inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/* 跳转到选中的适配器 */
for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
/* 打开设备 */
if ( (adhandle= pcap_open(
d->name, // 设备名
65536, // 保证能捕获到数据链路层上的每个数据包的全部内容
PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
1000, // 读取超时时间
NULL, // 远程机器验证
errbuf // 错误缓冲池
) ) == NULL)
{
fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -1;
}
/*在选中的设备接口上监听数据*/
printf("\nlistening on %s...\n", d->description);
/* 释放设备列表 */
pcap_freealldevs(alldevs);
/* 开始捕获 */
struct pcap_pkthdr *header;
const u_char *pkt_data;
int res=-1;
struct tm *ltime;
char timestr[16];
time_t local_tv_sec;
/* 获取数据包 */
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);
printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
}
if(res == -1){
printf("Error reading the packets: %s\n", pcap_geterr(adhandle));
pcap_close(adhandle);
return -1;
}
pcap_close(adhandle);
return 0;
}