Qt下通過packet庫實現ARP資料包的傳送和接收
Qt中暫時據我瞭解暫時沒有對底層網絡卡操作的類和相關庫,這次通過ARP協議寫的區域網ip搜尋程式都是採用微軟的底層網絡卡操作相關庫,此次主要了libpacket.a和libwpcap.a庫。操作步驟如下:
(2) 到http://www.winpcap.org/devel.htm下載開發包,把winpcap開發包中的Include目錄下的所有內容拷貝到qt的\Qt\4.8.1\mingw\include目錄下。
(3) 把winpcap開發包中的Lib目錄下的Packet.a和wpcap.a拷貝到\Qt\4.8.1\mingw\lib目錄下。但是我感覺那樣沒有成功,我是拷貝到工程目錄下
LIBS+=$$PWD/libpacket.a\
$$PWD/libwpcap.a
還有就是要在庫檔案中新增#include"Packet32.h"標頭檔案(參考網址http://blog.csdn.net/q5707802/article/details/38373929)
(4) 這樣相關的庫和標頭檔案都準備好了開始程式編寫。實現ARP資料包的傳送和接收主要為獲取本機網絡卡資訊、開啟網絡卡、將封裝好的資料包傳送出去、接收返回的ARP資料包、資料解析、關閉網絡卡等步驟。步驟看著不多,但是裡面有很多細節實現起來還是比較麻煩。也是費了很大的力氣才把程式除錯好。下面一步一步來講解Qt下實現ip搜尋的步驟
(5) 獲取網絡卡資訊
在IP搜尋程式中主要用到了網絡卡名稱,網絡卡的MAC地址,IP到是沒有用到。這裡主要有兩種方法實現網絡卡資訊的獲取。其一通過Packet32.h中的函式PacketGetAdapterNames()函式獲取,這個還沒有嘗試過;其二通過Qt自帶的類QNetworkInterface,這個類可以獲取網絡卡的所有資訊,程式碼實現如下
QList<QNetworkInterface>listMac=netWorkInerface.allInterfaces();
這一句程式碼就把網絡卡的所有資訊都獲取到了,現在只需要通過listMac輸出即可,例如
listMac.
localMacName=listMac.at(0).name();//獲取本機MAC名稱
使用起來很方便,這樣就獲取到了網絡卡資訊。但是這時還要對獲取到網絡卡資訊進行轉換,網絡卡MAC要轉換成16進位制數才能進行下一步的操作。這裡要提示的是網絡卡名稱其實可以不用做其它操作就可以在下面的步驟中使用了,但是通過packet庫傳送ARP資料包在開啟網絡卡時使用NPF方式,所以在獲取到網絡卡名稱中後新增如下程式碼:
localMacName="\\Device\\NPF_"+localMacName;
(6) 開啟網絡卡
開啟網絡卡到是很簡答,通過PacketOpenAdapter(網絡卡名稱),如果開啟成功返回開啟的網絡卡控制代碼,如果開啟失敗返回NULL。這裡提示一定要儲存次網絡卡控制代碼,以便在後面使用
(7) 封裝資料包
這裡比較重要的是要把封裝的資料包的格式轉換為運輸層要傳送的資料包格式,可以通過htons()函式實現,htons主要功能是將無符號短整型轉換成網路位元組。
(8) 傳送資料包
這裡將用到兩個比較重要的結構體
資料結構:_ADAPTER(關於Network Adapter的)
typedef struct _ADAPTER
{
HANDLE hFile; // 一個開啟的NPF driver例項的控制代碼:
CHAR SymbolicLink[MAX_LINK_NAME_LENGTH]; // 當前開啟的網絡卡的名字:
int NumWrites; // 在這塊Adapter上,一個數據包被寫的次數:
HANDLE ReadEvent; /* 這塊Adapter上的read操作的通知事件。它可以被傳遞給標準Win32函式(如WaitForSingleObject或者WaitForMultipleObjects),這樣可以等待到driver的緩衝區內有資料到來。在同時等待幾個事件的GUI程式中,它特別有用。在Windows2000/XP中,函式PacketSetMinToCopy()可以用來設定核心緩衝區中激發本事件的最小資料大小:*/
UINT ReadTimeOut; // 設定一個時間,到時候,即使沒有捕獲任何包,read操作也會被釋放,ReadEvent也會被觸發:
} ADAPTER, *LPADAPTER;
資料結構:_PACKET(關於Packet的)
typedef struct _PACKET
{
HANDLE hEvent; // 向後相容用的:
OVERLAPPED OverLapped; // 向後相容用的:
PVOID Buffer; // 存放Packets的緩衝區:
UINT Length; // 緩衝區的大小:
DWORD ulBytesReceived; // 當前緩衝區中有效的位元組數,如,上一次呼叫PacketReceivePacket()函式接收到的位元組數:
BOOLEAN bIoComplete // 向後相容用的:
} PACKET, *LPPACKET;
傳送程式碼如下,傳送前提是資料包已經封裝好了
lpPacket=PacketAllocatePacket();//給PACKET結構指標分配記憶體
if(lpPacket==NULL)
{
PacketCloseAdapter(adapterHandle);
returnfalse;
}
PacketInitPacket(lpPacket,&broadcastARPcmd,sizeof(broadcastARPcmd));
if(PacketSetNumWrites(adapterHandle,1)==0)//每次只發送一個包
{
PacketFreePacket(lpPacket);
PacketCloseAdapter(adapterHandle);
returnfalse;
}
BOOLflag=PacketSendPacket(adapterHandle,lpPacket,true);
//判斷是否傳送成功
if(flag==FALSE)
{
returnfalse;
}
這裡面有幾個函式PacketAllocatePacket()給PACKET結構指標分配記憶體,這個函
函式必須要要不然資料傳送不出去。
PacketInitPacket(lpPacket,&broadcastARPcmd,sizeof(broadcastARPcmd))其中lpPacket為_PACKET結構體指標,broadcastARPcmd為要傳送資料包,這個函式必須有,PacketSetNumWrites()設定傳送次數可以沒有,預設傳送一次。最後通過PacketSendPacket(adapterHandle,lpPacket,true)傳送資料包,其中adapterHandle為網絡卡控制代碼,第二個引數說過了,第三個引數不用說了很明白。
這樣資料包就傳送出去了。我也是除錯了很長時間才除錯好的。
(9)解析資料包
前面的步驟完了算是做完了一大半了,解析資料包主要是對ARP廣播訊號非迴應資訊解析。這裡在winpcap開發包中有參考例子,可以進行參考的。我也是參考例程程式碼的。這裡不在詳細介紹。相關參考程式碼如下:
if(PacketSetHwFilter(adapterHandle,0x00000020)==FALSE)
{
qDebug("Warning:unabletosetpromiscuousmode!\n");
returnfalse;
}
if(PacketSetBuff(adapterHandle,500*1024)==false)//設定網絡卡快取大小
{
PacketFreePacket(lpPacket);
PacketCloseAdapter(adapterHandle);
returnfalse;
}
if(PacketSetReadTimeout(adapterHandle,100)==false)//設定超時時間
{
PacketFreePacket(lpPacket);
PacketCloseAdapter(adapterHandle);
returnfalse;
}
PacketInitPacket(lpPacket,(char*)recBuf,sizeof(recBuf));
while(readNULLFlag)
{
tempresult=PacketReceivePacket(adapterHandle,lpPacket,TRUE);//某個網絡卡來接受
if(tempresult==FALSE||lpPacket->ulBytesReceived<1)
{
tempresult=PacketReceivePacket(adapterHandle,lpPacket,TRUE);
if(tempresult==FALSE||lpPacket->ulBytesReceived<1)
{
readNULLFlag=false;
}
}
receiveDataPacket(lpPacket);
}
這裡說明的是PacketSetHwFilter(adapterHandle,0x00000020)中的0x00000020,這裡設定過濾模式為混雜模式,讀取網絡卡上收到的所有資訊,在MFC中用的是巨集定義,我在QT下查詢時沒有找到於是在MFC中查詢的巨集定義的值直接複製過來的。其中巨集定義為
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define NDIS_PACKET_TYPE_SMT 0x00000040
#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
#define NDIS_PACKET_TYPE_GROUP 0x00001000
#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00002000
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00004000
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00008000
#define NDIS_PACKET_TYPE_NO_LOCAL 0x00010000
其它巨集定義自己可以網上檢視相關說明不在一一說明。這些都設定完成後就可通過PacketReceivePacket()函式接收資料,最後通過receiveDataPacket()函式進行解析,這個函式是自己定義實現的。相關程式碼在winpcap開發包中有參考例子可供參考