不用回调函数来捕捉数据包
这节课举的例子很象上节课的例子(获得已经安装设备的高级信息),但是这个例子我们用的是函数pcap_next_ex()而不是用pcap_loop()。
在某些情况下面,基于回调函数来捕获数据包的pcap_loop()方面是很好的选择,但是,处理一个回调函数有时候是不现实的。通常它会使得程序变得非常复杂并且在多线程应用程序或者在C++类里面它会成为一块心病。
pcap_next_ex()方法可以让我们直接调用来获得数据包。使用pcap_next_ex()可以获得程序的完全控制,因为仅仅在程序要捕获数据包的时候数据包才会接收。
在下面的程序里面,调用完pcap_next_ex(),我们重复使用上次课例子里面的回调函数代码,然后在main函数里面去掉他们。
函数 int pcap_next_ex ( pcap_t * p,
struct pcap_pkthdr ** pkt_header,
const u_char ** pkt_data )
作用:从一个接口上面读取数据包或者从一个离线捕捉中读取数据包。该函数经常用来获取下一个可用的数据包,而避免使用LibPcap提供的传统的使用回调函数的方法。
参数:
pcap_t * 在头文件里面有定义:pcap_t就是pcap的别名。
参数前面都已经出现过,这里就不做详细介绍了。
返回值:
返回1:如果读取数据包没有出现错误。
返回0:如果pcap_open_live()设置的timeout时间到了。此时,ptk_header和pkt_data不会指向一个合法的数据包。
返回-1:发生错误
返回-2:从一次离线捕获中读取数据遇到了EOF。
【Code】
00001 #include "pcap.h"
00002
00003
00004 main()
00005 {
00006 pcap_if_t *alldevs;
00007 pcap_if_t *d;
00008 int inum;
00009 int i=0;
00010 pcap_t *adhandle;
00011 int res;
00012 char errbuf[PCAP_ERRBUF_SIZE];
00013 struct tm *ltime;
00014 char timestr[16];
00015 struct pcap_pkthdr *header;
00016 u_char *pkt_data;
00017
00018
00019 /* Retrieve the device list on the local machine */
00020 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
00021 {
00022 fprintf(stderr,"Error in pcap_findalldevs: %s/n", errbuf);
00023 exit(1);
00024 }
00025
00026 /* Print the list */
00027 for(d=alldevs; d; d=d->next)
00028 {
00029 printf("%d. %s", ++i, d->name);
00030 if (d->description)
00031 printf(" (%s)/n", d->description);
00032 else
00033 printf(" (No description available)/n");
00034 }
00035
00036 if(i==0)
00037 {
00038 printf("/nNo interfaces found! Make sure WinPcap is installed./n");
00039 return -1;
00040 }
00041
00042 printf("Enter the interface number (1-%d):",i);
00043 scanf("%d", &inum);
00044
00045 if(inum < 1 || inum > i)
00046 {
00047 printf("/nInterface number out of range./n");
00048 /* Free the device list */
00049 pcap_freealldevs(alldevs);
00050 return -1;
00051 }
00052
00053 /* Jump to the selected adapter */
00054 for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
00055
00056 /* Open the device */
00057 if ( (adhandle= pcap_open(d->name, // name of the device
00058 65536, // portion of the packet to capture.
00059 // 65536 guarantees that the whole packet will be captured on all the link layers
00060 PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
00061 1000, // read timeout
00062 NULL, // authentication on the remote machine
00063 errbuf // error buffer
00064 ) ) == NULL)
00065 {
00066 fprintf(stderr,"/nUnable to open the adapter. %s is not supported by WinPcap/n", d->name);
00067 /* Free the device list */
00068 pcap_freealldevs(alldevs);
00069 return -1;
00070 }
00071
00072 printf("/nlistening on %s.../n", d->description);
00073
00074 /* At this point, we don't need any more the device list. Free it */
00075 pcap_freealldevs(alldevs);
00076
00077 /* Retrieve the packets */
00078 while((res = pcap_next_ex( adhandle, &header, &pkt_data)) >= 0){
00079
00080 if(res == 0)
00081 /* Timeout elapsed */
00082 continue;
00083
00084 /* convert the timestamp to readable format */
00085 ltime=localtime(&header->ts.tv_sec);
00086 strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
00087
00088 printf("%s,%.6d len:%d/n", timestr, header->ts.tv_usec, header->len);
00089 }
00090
00091 if(res == -1){
00092 printf("Error reading the packets: %s/n", pcap_geterr(adhandle));
00093 return -1;
00094 }
00095
00096 return 0;
00097 }
程序运行说明:按照前面所说的加上宏定义即可。
疑问:没有完全体现出好控制的特征
感觉也只是点点,可以设置一个变量,来控制是否接收下一个数据包,这点还是可以做到得,不过这里是因为数据包太多了,一般不会出现问题。关于pcap_next_ex()方法,文档里面没有介绍是阻塞得还是非阻塞,个人认为是阻塞得,也就是说从开始执行,一直倒接收倒一个包才返回。尽管这里是阻塞得,但是设置一个变量每次检查一下,看看是否是用户要求关闭接收,这个操作还是没什么问题。不会出现虽然用户设置了停止阻塞得标志位,但是由于此方法是阻塞的,因为等不到数据包而一直没有响应用户,因为这里有一个事实大家应该明白,网上发送的数据包时时刻刻都在。
注意:之所以要使用pcap_next_ex()而不使用pcap_next()是因为pcap_next()有些非常讨厌的限制。首先,他不是很有效的方法,因为它隐藏了回调方法,但是还是依靠pcap_dispatch()。第二,它不能探测出EOF,所以在合并文件时它几乎没用。
另外,pcap_next_ex()方法的返回值可以给用户很多的提示,对应的所有可能情况。