winPcap編程之打開適配器並捕獲數據包(四 轉)

在貼源碼以前先介紹一個將要用到的很重要的函數--pcap_open(),下面是pcap_open()在remote-ex.h中的聲明:網絡

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

第一個參數不用多說,它表示的是設備的名稱。在獲取適配器鏈表後,經過返回數據域name便可知道設備的名稱。app

      第二個參數制定要捕獲數據包中的哪些部分。技術文檔中介紹說,在一些操做系統中,驅動能夠被配置成只捕獲數據包的初始化部分,它的好處就是能夠減小應用程序間複製數量的量,從而提升捕獲效率。下面將要給出的實例程序中設爲65535。咱們知道對於使用以太網的局域網來講,最大傳輸單元爲1500字節,那麼設爲65535則能保證收到完整的數據包。函數

      第三個參數是最重要的一個值,它用來指示適配器是否須要設置成混雜模式,這裏引用一下技術文檔中對設置混雜模式的說明:oop

通常狀況下,適配器只接收發給它本身的數據包, 而那些在其餘機器之間通信的數據包,將會被丟棄。 相反,若是適配器是混雜模式,那麼無論這個數據包是否是發給個人,我都會去捕獲。也就是說,我會去捕獲全部的數據包。 這意味着在一個共享媒介(好比總線型以太網),WinPcap能捕獲其餘主機的全部的數據包。 大多數用於數據捕獲的應用程序都會將適配器設置成混雜模式,因此,咱們也會在下面的範例中,使用混雜模式。spa

      它的意思就是說設置混雜模式能夠捕獲到全部通過適配器的數據包,不管是不是發給機器自己的。PCAP_OPENFLAG_PROMISCUOUS這個值就是設置成混雜模式的意思。操作系統

      第四個參數表示的是讀取數據的超時時間,單位是毫秒。意思就是說會在read_timeout時間內對適配器的讀取操做進行響應,無論有沒有讀到數據。這裏有兩個特殊的值須要說明一下,若是將時間設置爲0意味着沒有超時,那麼若是沒有數據到達的話,讀操做就永遠不會返回;若是設置爲-1則偏偏相反,不論有沒有讀到數據都會當即返回。翻譯

      第五個參數以前提到過,它表示的是鏈接遠程用戶的驗證信息,因爲咱們不須要鏈接到遠程用戶,這裏置爲NULL。指針

      第六個參數是錯誤信息緩衝,若是該函數在調用過程當中出錯則會將出錯信息保存在緩衝中。code

      函數的返回值是一個pcap_t的指針類型,查看聲明處咱們能夠發現pcap_t其實是pcap結構體,而文檔上說明它是一個已打開的捕捉實例的描述符。這個結構體對用戶來講是不透明的(咱們查看pcap的聲明處是找不到的),它經過wpcap.dll提供的函數,維護了它的內容。blog

      下面咱們來看一下這個程序的代碼!

 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=0;
16     pcap_t *adhandle;
17     char errbuf[PCAP_ERRBUF_SIZE];
18 
19     /* 獲取本機設備列表 */
20     if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
21     {
22         fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
23         exit(1);
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==0)
37     {
38         printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
39         return -1;
40     }
41 
42     printf("Enter the interface number (1-%d):",i);
43     scanf("%d", &inum);
44 
45     if(inum < 1 || inum > i)
46     {
47         printf("\nInterface number out of range.\n");
48         /* 釋放設備列表 */
49         pcap_freealldevs(alldevs);
50         return -1;
51     }
52 
53     /* 跳轉到選中的適配器 */
54     for(d=alldevs, i=0; i< inum-1 ; d=d->next, i++);
55 
56     /* 打開設備 */
57     if ( (adhandle= pcap_open(d->name,          // 設備名
58                               65535,            // 65535保證能捕獲到不一樣數據鏈路層上的每一個數據包的所有內容
59                               PCAP_OPENFLAG_PROMISCUOUS,    // 混雜模式
60                               1000,             // 讀取超時時間
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 -1;
69     }
70 
71     printf("\nlistening on %s...\n", d->description);
72 
73     /* 釋放設備列表 */
74     pcap_freealldevs(alldevs);
75 
76     /* 開始捕獲 */
77     pcap_loop(adhandle, 0, packet_handler, NULL);
78 
79     return 0;
80 }
81 
82 
83 /* 每次捕獲到數據包時,libpcap都會自動調用這個回調函數 */
84 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
85 {
86     struct tm *ltime;
87     char timestr[16];
88     time_t local_tv_sec;
89 
90     /* 將時間戳轉換成可識別的格式 */
91     local_tv_sec = header->ts.tv_sec;
92     ltime=localtime(&local_tv_sec);
93     strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);
94 
95     printf("%s,%.6ld len:%d\n", timestr, header->ts.tv_usec, header->len);
96 
97 }

 

  引用一下文檔裏面對於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()函數返回時會自動調用這個回調函數。這個回調函數的參數類型是規定好的:

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

      這個程序當中咱們只用到了第二個參數,將每個數據包的時間戳和長度從它的首部當中解析出來,並打印在屏幕上。細節的東西就不贅述了,看一下運行的結果:

 輸入6並按回車,開始捕獲數據並打印:

相關文章
相關標籤/搜索