统计引擎利用了内核级的数据包过滤器,来有效地为收集到的数据包进行分类。为了使用这个特性,编程人员必须打开一个适配器,并且,可以使用pcap_setmode()将它设置为统计模式(statistical mode)。特别注意,必须使用MODE_STAT来作为这个函数的mode参数。
在统计模式下,编写一个用于监听TCP网络流量的程序并不复杂,代码也不多。
实现源码:
[cpp]
view plain
copy
print
?
-
-
-
-
-
-
-
-
-
-
- #include <pcap.h>
- #include <remote-ext.h>
- #pragma comment(lib,"Packet.lib")
- #pragma comment(lib,"wpcap.lib")
- #pragma comment(lib,"ws2_32.lib")
- void usage();
- void dispatcher_handler( u_char *, const struct pcap_pkthdr *, const u_char * );
- void main( int argc, char **argv)
- {
- pcap_t *fp;
- char error[PCAP_ERRBUF_SIZE];
- struct timeval st_ts;
- u_int netmask;
- struct bpf_program fcode;
-
- if ( argc != 2 )
- {
- usage();
- return;
- }
-
- if ( ( fp = pcap_open_live( argv[1], 100, 1, 1000, error ) ) == NULL )
- {
- fprintf_s( stderr, "/n适配器打开失败: %s/n", error );
- return;
- }
-
- netmask=0xffffff;
-
- if ( pcap_compile( fp, &fcode, "tcp", 1, netmask ) < 0 )
- {
- fprintf_s( stderr,"/n不能编译包过滤器,检查语法./n" );
-
- return;
- }
-
- if ( pcap_setfilter( fp, &fcode ) < 0 )
- {
- fprintf_s( stderr, "/n过滤器设置失败./n" );
-
- return;
- }
-
- pcap_setmode( fp, MODE_STAT );
- printf_s( "TCP 流量统计:/n" );
-
- pcap_loop( fp, 0, dispatcher_handler, (PUCHAR)&st_ts );
- pcap_close(fp);
- return;
- }
- void dispatcher_handler( u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data )
- {
- struct timeval *old_ts = (struct timeval *)state;
- u_int delay;
- LARGE_INTEGER Bps,Pps;
- struct tm ltime;
- char timestr[16];
-
-
- delay =(header->ts.tv_sec - old_ts->tv_sec) * 1000000 - old_ts->tv_usec + header->ts.tv_usec;
-
- Bps.QuadPart = (((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay));
-
-
-
-
-
-
-
-
-
- Pps.QuadPart = (((*(LONGLONG*)(pkt_data)) * 1000000) / (delay));
-
- time_t t = (time_t )&header->ts.tv_sec;
- localtime_s( <ime, &t );
- strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);
- printf_s("%s,/t%.6d毫秒/t长度:%d/n", timestr, header->ts.tv_usec, header->len );
-
- printf_s("%s ", timestr);
-
- printf_s( "BPS=%I64u ", Bps.QuadPart );
- printf_s( "PPS=%I64u/n", Pps.QuadPart );
-
- old_ts->tv_sec=header->ts.tv_sec;
- old_ts->tv_usec=header->ts.tv_usec;
- }
- void usage()
- {
- printf_s("/nUsage:/n");
- printf_s("/t tcptop adapter/n");
- printf_s("/t You can use /"WinDump -D/" if you don‘t know the name of your adapters./n");
- exit(0);
- }
///////////////////////////////////////////////////////////////////////////// // Name: TrafficCount.cpp // Purpose: wincap收集并统计网络流量。 // Compiler: VS2005 // Author: 陈相礼 // Modified by: // Created: 09/18/09 // Copyright: // Licence: ///////////////////////////////////////////////////////////////////////////// #include <pcap.h> #include <remote-ext.h> #pragma comment(lib,"Packet.lib") #pragma comment(lib,"wpcap.lib") #pragma comment(lib,"ws2_32.lib") void usage(); void dispatcher_handler( u_char *, const struct pcap_pkthdr *, const u_char * ); void main( int argc, char **argv) { pcap_t *fp; char error[PCAP_ERRBUF_SIZE]; struct timeval st_ts; u_int netmask; struct bpf_program fcode; /* 检查命令行参数的合法性 */ if ( argc != 2 ) { usage(); return; } /* 打开输出适配器 */ if ( ( fp = pcap_open_live( argv[1], 100, 1, 1000, error ) ) == NULL ) { fprintf_s( stderr, "/n适配器打开失败: %s/n", error ); return; } /* 不用关心掩码,在这个过滤器中,它不会被使用 */ netmask=0xffffff; // 编译过滤器 if ( pcap_compile( fp, &fcode, "tcp", 1, netmask ) < 0 ) { fprintf_s( stderr,"/n不能编译包过滤器,检查语法./n" ); /* 释放设备列表 */ return; } // 设置过滤器 if ( pcap_setfilter( fp, &fcode ) < 0 ) { fprintf_s( stderr, "/n过滤器设置失败./n" ); /* 释放设备列表 */ return; } /* 将网卡设置为统计模式 */ pcap_setmode( fp, MODE_STAT ); printf_s( "TCP 流量统计:/n" ); /* 开始主循环 */ pcap_loop( fp, 0, dispatcher_handler, (PUCHAR)&st_ts ); pcap_close(fp); return; } void dispatcher_handler( u_char *state, const struct pcap_pkthdr *header, const u_char *pkt_data ) { struct timeval *old_ts = (struct timeval *)state; u_int delay; LARGE_INTEGER Bps,Pps; struct tm ltime; char timestr[16]; /* 以微妙计算上一次采样的延迟时间 */ /* 这个值通过采样到的时间戳获得 */ delay =(header->ts.tv_sec - old_ts->tv_sec) * 1000000 - old_ts->tv_usec + header->ts.tv_usec; /* 获得每秒的比特数 */ Bps.QuadPart = (((*(LONGLONG*)(pkt_data + 8)) * 8 * 1000000) / (delay)); /* ^ ^ | | | | | | 将字节转换成比特 -- | | 延时是以微妙表示的 -- */ /* 获得每秒的数据包数 */ Pps.QuadPart = (((*(LONGLONG*)(pkt_data)) * 1000000) / (delay)); /* 将时间戳转变为易读的标准格式*/ time_t t = (time_t )&header->ts.tv_sec; localtime_s( <ime, &t ); strftime( timestr, sizeof timestr, "%H:%M:%S", <ime); printf_s("%s,/t%.6d毫秒/t长度:%d/n", timestr, header->ts.tv_usec, header->len ); /* 打印时间戳*/ printf_s("%s ", timestr); /* 打印采样结果 */ printf_s( "BPS=%I64u ", Bps.QuadPart ); printf_s( "PPS=%I64u/n", Pps.QuadPart ); //存储当前的时间戳 old_ts->tv_sec=header->ts.tv_sec; old_ts->tv_usec=header->ts.tv_usec; } void usage() { printf_s("/nUsage:/n"); printf_s("/t tcptop adapter/n"); printf_s("/t You can use /"WinDump -D/" if you don‘t know the name of your adapters./n"); exit(0); }
在设置为统计模式前可以设置一个过滤器来指定要捕获的协议包。如果没有设置过滤器那么整个网络数据都将被监视。一旦设置了过滤器就可以调用pcap_setmode()来设置为统计模式,之后网卡开始工作在统计模式下。
需要指出的是pcap_open_live()的第四个参数(to_ms)定义了采样的间隔,回调函数pcap_loop()每隔一定间隔就获取一次采样统计,这个采样被装入pcap_loop()的第二和第三个参数,过程如下图所示:
____________________
|struct timeval ts |
|__________________|
|bpf_u_int32 |
|caplen=16 | struct pcap_pkthdr*
|__________________| (参数2)
| bpf_u_int32 |
| len=16 |
|__________________|
________________________________
|large_integer Accepted packet |
|______________________________| uchar *
| large_integer Accepted bits | (参数3)
|______________________________|
用两个64位的计数器分别记录最近一次间隔数据包数量和比特数量。
本例子中,网卡打开时设置超时为1000毫秒,也就是说dispatcher_handler()每隔1秒就被调用一次。过滤器也设置为只监视TCP包,然后pcap_setmode() and pcap_loop()被调用,注意一个指向timeval的指针作为参数传送到pcap_loop()。这个timeval结构将用来存储个时间戳以计算两次采样的时间间隔。
dispatcher_handler()用该间隔来获取每秒的比特数和数据包数,并把着两个数显示在显示器上。
最后指出的是目前这个例子是比任何一个利用传统方法在用户层统计的包捕获程序都高效。因为统计模式需要最小数量的数据拷贝和上下环境交换,同时还有最小的内存需求,所以CPU是最优的。
此文章来自于【
http://blog.csdn.net/eaglewood2005/article/details/4567727
】