在贴源码之前先介绍一个将要用到的很重要的函数--pcap_open(),下面是pcap_open()在remote-ex.h中的声明:

  1. pcap_t *pcap_open(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);

第一个参数不用多说,它表示的是设备的名称。在获取适配器链表后,通过返回数据域name即可知道设备的名称。

第二个参数制定要捕获数据包中的哪些部分。技术文档中介绍说,在一些操作系统中,驱动可以被配置成只捕获数据包的初始化部分,它的好处就是可以减少应用程序间复制数量的量,从而提高捕获效率。下面将要给出的实例程序中设为65535。我们知道对于使用以太网的局域网来说,最大传输单元为1500字节,那么设为65535则能保证收到完整的数据包。

第三个参数是最重要的一个值,它用来指示适配器是否需要设置成混杂模式,这里引用一下技术文档中对设置混杂模式的说明:

一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃。 相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。 这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。

它的意思就是说设置混杂模式可以捕获到所有经过适配器的数据包,不论是不是发给机器本身的。PCAP_OPENFLAG_PROMISCUOUS这个值就是设置成混杂模式的意思。

第四个参数表示的是读取数据的超时时间,单位是毫秒。意思就是说会在read_timeout时间内对适配器的读取操作进行响应,不管有没有读到数据。这里有两个特殊的值需要说明一下,如果将时间设置为0意味着没有超时,那么如果没有数据到达的话,读操作就永远不会返回;如果设置为-1则恰恰相反,不论有没有读到数据都会立即返回。

第五个参数之前提到过,它表示的是连接远程用户的验证信息,由于我们不需要连接到远程用户,这里置为NULL。

第六个参数是错误信息缓冲,如果该函数在调用过程中出错则会将出错信息保存在缓冲中。

函数的返回值是一个pcap_t的指针类型,查看声明处我们可以发现pcap_t实际上是pcap结构体,而文档上说明它是一个已打开的捕捉实例的描述符。这个结构体对用户来说是不透明的(我们查看pcap的声明处是找不到的),它通过wpcap.dll提供的函数,维护了它的内容。

下面我们来看一下这个程序的代码!

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #define HAVE_REMOTE
  5. #include <pcap.h>
  6.  
  7. /* packet handler 函数原型 */
  8. void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
  9.  
  10. int main()
  11. {
  12. pcap_if_t *alldevs;
  13. pcap_if_t *d;
  14. int inum;
  15. int i=;
  16. pcap_t *adhandle;
  17. char errbuf[PCAP_ERRBUF_SIZE];
  18.  
  19. /* 获取本机设备列表 */
  20. if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -)
  21. {
  22. fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
  23. exit();
  24. }
  25.  
  26. /* 打印列表 */
  27. for(d=alldevs; d; d=d->next)
  28. {
  29. printf("%d. %s", ++i, d->name);
  30. if (d->description)
  31. printf(" (%s)\n", d->description);
  32. else
  33. printf(" (No description available)\n");
  34. }
  35.  
  36. if(i==)
  37. {
  38. printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
  39. return -;
  40. }
  41.  
  42. printf("Enter the interface number (1-%d):",i);
  43. scanf("%d", &inum);
  44.  
  45. if(inum < || inum > i)
  46. {
  47. printf("\nInterface number out of range.\n");
  48. /* 释放设备列表 */
  49. pcap_freealldevs(alldevs);
  50. return -;
  51. }
  52.  
  53. /* 跳转到选中的适配器 */
  54. for(d=alldevs, i=; i< inum- ; d=d->next, i++);
  55.  
  56. /* 打开设备 */
  57. if ( (adhandle= pcap_open(d->name, // 设备名
  58. , // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
  59. PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
  60. , // 读取超时时间
  61. NULL, // 远程机器验证
  62. errbuf // 错误缓冲池
  63. ) ) == NULL)
  64. {
  65. fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
  66. /* 释放设备列表 */
  67. pcap_freealldevs(alldevs);
  68. return -;
  69. }
  70.  
  71. printf("\nlistening on %s...\n", d->description);
  72.  
  73. /* 释放设备列表 */
  74. pcap_freealldevs(alldevs);
  75.  
  76. /* 开始捕获 */
  77. pcap_loop(adhandle, , packet_handler, NULL);
  78.  
  79. return ;
  80. }
  81.  
  82. /* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
  83. void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
  84. {
  85. struct tm *ltime;
  86. char timestr[];
  87. time_t local_tv_sec;
  88.  
  89. /* 将时间戳转换成可识别的格式 */
  90. local_tv_sec = header->ts.tv_sec;
  91. ltime=localtime(&local_tv_sec);
  92. strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
  93.  
  94. printf("%s,%.6ld len:%d\n", timestr, header->ts.tv_usec, header->len);
  95.  
  96. }

引用一下文档里面对于pcap_loop()函数的介绍:

Collect a group of packets.

pcap_loop() is similar to pcap_dispatch() except it keeps reading packets until cnt packets are processed or an error occurs. It does not return when live read timeouts occur. Rather, specifying a non-zero read timeout to pcap_open_live() and then callingpcap_dispatch() allows the reception and processing of any packets that arrive when the timeout occurs. A negative cnt causespcap_loop() to loop forever (or at least until an error occurs). -1 is returned on an error; 0 is returned if cnt is exhausted; -2 is returned if the loop terminated due to a call to pcap_breakloop() before any packets were processed. If your application uses pcap_breakloop(), make sure that you explicitly check for -1 and -2, rather than just checking for a return value < 0.

大致就是说pcap_loop()函数是用来捕获一组数据分组的。pcap_loop()函数跟pcap_dispath()函数很类似,唯一不同之处就是pcap_loop()会一直读数据直到cnt数据被处理或者出现错误。不论读取时间是否超时它都不返回。当然有一种特殊情况,就是在调用pcap_open_live()函数时指定非0的读取超时时间,调用pcap_dispath()函数可以在超时发生时对读到的数据进行接收和处理。将cnt设为正数可以使pcap_loop()一直循环(或至少到错误发生之前)。返回-1则出错;返回0则说明cnt被耗尽;返回-2则是由于在数据被处理之前调用pcap_breakloop()来中断循环。如果你的应用程序使用了pcap_breakloop()函数,你需要对返回值-1和-2进行详细的检查,而不是只检查<0的情况。(如果翻译有误还请指正!!!)

文档里说得很详细,看得也是一头雾水,也不知道究竟在说些什么。简单来讲就是说,pcap_loop()只有当cnt数据包被捕获时才会返回,即它会在一小段时间内阻塞网络的利用。pcap_loop()函数的第三个参数很重要,它是一个回调函数的函数指针,即pcap_loop()函数返回时会自动调用这个回调函数。这个回调函数的参数类型是规定好的:

  1. typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
    const u_char *);

这个程序当中我们只用到了第二个参数,将每一个数据包的时间戳和长度从它的首部当中解析出来,并打印在屏幕上。细节的东西就不赘述了,看一下运行的结果:

输入6并按回车,开始捕获数据并打印:

winPcap编程之打开适配器并捕获数据包(四 转)的更多相关文章

  1. winPcap_5_打开适配器并捕获数据包

    知道如何获取适配器的信息了,那我们就开始一项更具意义的工作,打开适配器并捕获数据包.编写一个程序,将每一个通过适配器的数据包打印出来. 打开设备的函数是 pcap_open(). (Open a ge ...

  2. winPcap编程之不用回调方法捕获数据包(五 转)

    这一次要分析的实例程序跟上一讲非常类似(“打开适配器并捕获数据包”),略微不同的一点是本次将pcap_loop()函数替换成了pcap_next_ex()函数.本节的重点也就是说一下这两个函数之间的差 ...

  3. Winpcap笔记3之打开适配器并捕获数据包

    上一讲中知道了如何获取适配的信息,这一将我们讲写一个程序蒋每一个通过适配器的数据包打印出来. 打开设备的函数是pcap_open().函数原型是 pcap_t* pcap_open(const cha ...

  4. 【VS开发】使用WinPcap编程(4)——把网络数据包存储到一个文件中

    这里用到的数据结构是pcap_dumper_t,这也是一个相当于文件描述符的东西,我们在用的时候先指定pcap_dumper_t *dumpfp; 使用两个函数来存储网络数据,一个是pcap_dump ...

  5. winpcap使用之捕获数据包

    第一种方法,调用回调函数 #include "pcap.h" /* packet handler 函数原型 */ void packet_handler(u_char *param ...

  6. Winpcap笔记4之不用回调函数捕获数据包

    函数1: pcap_next_ex(pcap_t*                       p, struct pcap_pkthdr**   pkt_header, const u_char*  ...

  7. winPcap_6_不用回调方法捕获数据包

    用 pcap_next_ex() 函数代替 _5_ 中的 pcap_loop()函数: pcap_loop()函数是基于回调的原理来进行数据捕获,这是一种精妙的方法,并且在某些场合中,它是一种很好的选 ...

  8. Python3+pyshark捕获数据包并保存为文件

    一.直接使用wireshark捕获数据包并保存为文件 可以使用wireshark通过图形界面的操作来实现捕获数据包并保存为文件. wireshark默认捕获的数据包保存为临时文件,如果最后退出时不选择 ...

  9. Linux系统捕获数据包流程

    Linux系统捕获数据包流程 为了提高数据包的捕获效率,瓶颈问题是一个需要非常关注的焦点.减少在捕获数据包过程中的瓶颈,就能够提高数据包捕获的整体性能.下面本文将以Linux操作系统为平台,分析捕获数 ...

随机推荐

  1. 201521123075 《Java程序设计》第5周学习总结

    1. 本周学习总结 2. 书面作业 作业参考文件下载 1 .代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分 ...

  2. 201521123030《Java程序设计》第1周学习总结

    #1. 本章学习总结 你对于本章知识的学习总结 了解了java的发展简介 认识了jvm/jre/jdk的联系,学习安装jdk java好麻烦的说... 1.为什么java程序可以跨平台运行?执行jav ...

  3. 201521044091 java 第一周总结

    1.本周学习总结 (1)第一次开始接触java语言,有些用法还是和c和c++有点差异,需要不断去学习 (2)java其实不仅仅一种高级语言,它包括的还有它的整套体系. 2. 书面作业 1.为什么jav ...

  4. 201521123028《Java程序设计》第1周学习总结

    1. 本周学习总结 通过本周的学习: 1.了解了Java的发展史(从OAK转向Internet,并逐步发展至今的历程) 2.与C语言比较,Java语言的特点: ①将源程序编译成一种结构中立的中间文件格 ...

  5. 201521123062《Java程序设计》第1周学习总结

    1.本章学习总结 认识Java平台运行环境,运行软件 初步认识JDK,JRE,JVM基本含义 书面作业 1.为什么java程序可以跨平台运行?执行java程序的步骤是什么? Java平台运行在各平台的 ...

  6. Git与码云(Git@OSC)入门-如何在实验室和宿舍同步你的代码(1)

    0.几个基本概念 本地仓库:本机上某个存放代码的仓库. 远程仓库:码云服务器上的代码仓库. 重要提醒:当我们在本地操作(新增.删除.修改)文件.目录时,并将其提交(commit),就是提交到了本地仓库 ...

  7. java第九次学习总结

    1. 本周学习总结 2.. 书面作业 1.常用异常 题目5-1 1.1 提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? 以前编写的代码经常出现异 ...

  8. 201521123022 《Java程序设计》 第十一周学习总结

    1.本章学习总结 2.书面作业 本次PTA作业题集多线程 Q1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问 Q1.1 除了使用synchronized修饰方法实现互斥同步访问,还 ...

  9. PowerBI开发 第四篇:DAX表达式

    DAX 表达式主要用于创建度量列(Measure),度量值是根据用户选择的Filter和公式,计算聚合值,DAX表达式基本上都是引用对应的函数,函数的执行有表级(Table-Level)上下文和行级( ...

  10. Spring第八篇【XML、注解实现事务控制】

    前言 本博文主要讲解Spring的事务控制,如何使用Spring来对程序进行事务控制-. 一般地,我们事务控制都是在service层做的..为什么是在service层而不是在dao层呢??有没有这样的 ...