11.1.2 统计实例
为了使用网络流量统计特性,编程人员必须打开一个网络适配器,并使用pcap_setmode函数将它设置为统计模式(MODE_STAT),同时使用MODE_STAT作为该函数mode参数的值。
1.网络流量统计的实例
下面的实例是在统计模式下编写一个用于监听TCP网络流量的程序,代码如下所示。完整的代码参见[ch11/Statistical工程]。
define WIN32
defne HAVE_REMOTE
include"pcap.h"
include<winsock.h>
include<stdio.h>
/读取信息的回调函数/
void dispatcher_handler(u_char,const struct pcap_pkthdr,
const u_char*);
int main(int argc,char**argv)
{
pcap_t*fp;
struct timeval st_ts;
u_int netmask;
struct bpf_program fcode;
……
/打开本机一个合适的网络设备/
……
/在这个过滤器中,不使用掩码/
netmask=0xffffff;
/编译过滤器,使其接收TCP协议的数据包/
if(pcap_compile(fp,&fcode,"tcp",1,netmask)<0)
{
fprintf(stderr,"\nUnable to compile the packet flter.
Check the syntax.\n");
pcap_close(fp);
pcap_freealldevs(alldevs);
return-1;
}
/设置过滤器/
if(pcap_setflter(fp,&fcode)<0)
{
fprintf(stderr,"\nError setting the flter.\n");
pcap_close(fp);
pcap_freealldevs(alldevs);
return-1;
}
/将接口设置为统计模式/
if(pcap_setmode(fp,MODE_STAT)<0)
{
fprintf(stderr,"\nError setting the mode.\n");
pcap_close(fp);
pcap_freealldevs(alldevs);
return-1;
}
printf("TCP traffc summary:\n");
/开始主循环/
pcap_loop(fp,0,dispatcher_handler,(PUCHAR)&st_ts);
return 0;
}
void dispatcher_handler(u_char*state,
const struct pcap_pkthdrheader,const u_charpkt_data)
{
struct timevalold_ts=(struct timeval)state;
u_int delay;
LARGE_INTEGER Bps,Pps;
struct tm*ltime;
char timestr[16];
time_t local_tv_sec;
/以微秒(µs)计算上一次采样的延迟时间,该值通过采样间的时间戳获得/
delay=(header->ts.tv_sec-old_ts->tv_sec)*1000000-
old_ts->tv_usec+header->ts.tv_usec;
/获取每秒的比特数bps/
//将字节转换成比特(8),延时是以微秒(1000000)表示的
Bps.QuadPart=
((((LONGLONG)(pkt_data+8))81000000)/(delay));
/得到每秒的数据包数量/
Pps.QuadPart=
((((LONGLONG)(pkt_data))*1000000)/(delay));
/将时间戳转换为可识别的格式,并打印/
local_tv_sec=header->ts.tv_sec;
ltime=localtime(&local_tv_sec);
strftime(timestr,sizeof timestr,"%H:%M:%S",ltime);
printf("%s",timestr);
/打印统计结果/
printf("BPS=%I64u",Bps.QuadPart);//bps
printf("PPS=%I64u\n",Pps.QuadPart);//包/s
/存储当前的时间戳/
old_ts->tv_sec=header->ts.tv_sec;
old_ts->tv_usec=header->ts.tv_usec;
}
在启动统计模式前,用户需要设置一个过滤器,以定义要监听的数据流,这里的过滤器被设置为只监视TCP协议的数据包(如果没有设置该过滤器,所有的数据流量都将会被监听)。然后调用pcap_setmode函数把接口描述符设置为统计模式,最后启动pcap_loop函数调用dispatcher_handler回调函数。
该实例程序的运行结果如图11-1所示。
图 11-1 网络流量统计的结果
注意pcap_open函数的第四个参数(to_ms),它定义了采样的时间间隔。回调函数将在每一个to_ms时间到期时,读取由NPF驱动程序提供的数据。这些数据将通过回调函数的第二个和第三个参数传递,参数的数据结构组成如图11-2所示。其中第二个参数主要提供了统计结束的时间戳。第三个参数提供了两个64位(8字节)的计数器(AcceptedPackets与AcceptedBytes),它们分别记录在最近的一个时间间隔内所收到的数据包的个数和字节总数。
图 11-2 回调函数参数的数据结构
上面实例设置的采样时间间隔为2000ms,这表示dispatcher_handler函数每隔2s就会被调用一次。
注意,实例中有一个指向timeval结构的指针,它被作为user参数传递给了pcap_loop函数。这个timeval结构体被用来存储时间戳,以便计算两次采样的时间间隔。dispatcher_handler函数则使用这个时间间隔来计算每秒的比特数(bps)以及每秒的数据包个数(pps),并将它们的值显示在屏幕上。
2.网络状态统计的实例
接下来的实例演示如何获得网络状态统计信息,包括所接收的数据包个数与所丢弃的数据包个数。
下面的实例程序每隔2000ms读取一次网络状态统计信息,并把所获得的信息显示到屏幕上,代码如下所示。完整的代码见[ch11/GetStats工程]。
defne WIN32
defne HAVE_REMOTE
include"pcap.h"
include"Win32-Extensions.h"
include<winsock.h>
include<stdio.h>
/读取与打印状态统计信息的回调函数/
void printfStats(pcap_t*p);
int main(int argc,char**argv)
{
pcap_t*fp;
struct timeval st_ts;
u_int netmask;
struct bpf_program fcode;
……
/打开本机一个合适的网络设备/
……
/在这个过滤器中,不会使用掩码/
netmask=0xffffff;
/编译过滤器,使其接收TCP协议的数据包/
if(pcap_compile(fp,&fcode,"tcp",1,netmask)<0)
{//出错,函数返回
……
return-1;
}
/设置过滤器/
if(pcap_setflter(fp,&fcode)<0)
{//出错,函数返回
……
return-1;
}
/将接口设置为统计模式/
if(pcap_setmode(fp,MODE_STAT)<0)
{//出错,函数返回
……
return-1;
}
/开始主循环/
while(1)
{
SleepEx(2000,TRUE);
printfStats(fp);
}
return 0;
}
void printfStats(pcap_t*p)
{
struct pcap_stat*pstats;
int stats_size=0;
struct tm*ltime;
char timestr[16];
time_t tm_sec;
/获得系统时间/
time(&tm_sec);
/将时间戳转换成可识别的格式/
ltime=localtime(&tm_sec);
strftime(timestr,sizeof timestr,"%H:%M:%S",ltime);
/获得状态统计信息/
pstats=pcap_stats_ex(p,&stats_size);
if(stats_size==sizeof(struct pcap_stat))
/打印时间与统计信息/
printf("%s:Stats:recv=%d,drop=%d\n",timestr,
pstats->ps_recv,pstats->ps_drop);
else
printf("error.\n");
}
该实例程序的执行结果如图11-3所示。
图 11-3 网络状态统计的结果