??? 距離上次發博文有兩三個星期的時候了,期間看了一些書,如《Effective C++》、《Windows核心編程》。感覺對計算機有了一個新的認識,自己以前對程序的見解是那么膚淺,完全沒有操作系統的概念,也沒有程序各部分與內存關系的認識。下面開始介紹這個類。
1. 類成員介紹?


#pragma ?once
#include? < vector >
#include? " pcap.h "
#pragma ?comment(lib,?"wpcap.lib")
#pragma ?comment(lib,?"Packet.lib")
using ? namespace ?std;
// ?定義回調函數指針
typedef? void ?( * pcap_back)(u_char? * ,? const ? struct ?pcap_pkthdr? * ,
????????????????? const ?u_char? * );
class ?CNetPacket
{
public :
????CNetPacket( void );
???? ~ CNetPacket( void );
???? // ?初始化網絡設備
???? int ?InitPcap( void );
???? // ?發送數據包
???? int ?SendPacket( const ?UCHAR * ,? int );
???? // ?返回設備列表的描述
????vector < CString > ?DevMsg( void );
???? // ?選定用于操作的設備
???? int ?SetDev( int );
???????? // ?傳入回調函數地址至此函數,第二個參數為接收包的個數
???? void ?GetPacket(pcap_back,? int );????
private :
???? char ?m_errBuf[PCAP_ERRBUF_SIZE];
????pcap_t? * m_pDevHandle;
????pcap_if_t? * m_pAllDevs;
};
??? 這個類包括了用于初始化網絡的InitPcap,用于返回網絡設備信息的DevMsg,用于選擇設備的SetDev,用于發送數據包的SendPacket和用于接收數據包的GetPacket。用戶必須先調用InitPcap,再調用SetDev,然后才能執行發送或接收。由于接收函數必采用回調方式才能工作,所以這里typedef了一個回調函數的指針void packet_handler(u_char *, const struct pcap_pkthdr *, const u_char *),它的三個參數都是pcap.h里頭的回調函數的參數。實際上,從WinPcap到用戶的使用共有兩次回調的過程,一是WinPcap提供的回調,另一個是封裝類的時候提供的回調。
2. 類的實現


#include? " StdAfx.h "
#include? " NetPacket.h "
CRITICAL_SECTION?g_cs;???? // ?全局變量的關鍵段
pcap_back?m_fp;???? // ?回調函數指針的成員變量
void ?packet_handler(u_char? * ,? const ? struct ?pcap_pkthdr? * ,? const ?u_char? * );???? // ?供WinPcap調用的回調函數
CNetPacket::CNetPacket( void )
{
????m_pDevHandle? = ?NULL;
????m_pAllDevs? = ?NULL;
}
CNetPacket:: ~ CNetPacket( void )
{
???? if (m_pDevHandle? != ?NULL)
????????pcap_close(m_pDevHandle);????
????DeleteCriticalSection( & g_cs);
}
// ?初始化網絡設備
int ?CNetPacket::InitPcap( void )
{
????InitializeCriticalSection( & g_cs);???? // 初始化關鍵段
???? if (pcap_findalldevs( & m_pAllDevs,?m_errBuf)? == ? - 1 )
???????? return ? - 1 ;
???? return ? 1 ;
}
// ?發送數據包,保證線程安全
int ?CNetPacket::SendPacket( const ?UCHAR? * data,? int ?length)
{
????EnterCriticalSection( & g_cs);???? // 進入關鍵段
???? if ?(pcap_sendpacket(m_pDevHandle,???? // ?Adapter
????????data,???????????????? // ?buffer?with?the?packet
????????length???????????????????? // ?size
????????)? != ? 0 )
????{
????????LeaveCriticalSection( & g_cs);???? // 退出關鍵段,不然其他線程不能進入
???????? return ? - 1 ;
????}
????LeaveCriticalSection( & g_cs);
???? return ? 1 ;
}
// ?返回設備列表的描述
vector < CString > ?CNetPacket::DevMsg( void )
{
????pcap_if_t? * d;
????vector < CString > ?devMsg;
????CString?temp;
???? for (d? = ?m_pAllDevs;?d;?d? = ?d -> next)
????{
????????temp? = ?d -> description;
????????devMsg.push_back(temp);
????}
???? return ?devMsg;
}
// ?選定用于操作的設備
int ?CNetPacket::SetDev( int ?devIndex)
{
????pcap_if_t? * d;
???? int ?i;
???? /* ?找到要選擇的網卡結構? */
???? for (d? = ?m_pAllDevs,?i? = ? 0 ;?i? < ?devIndex;?d? = ?d -> next,? ++ i);
???? /* ?打開選擇的網卡? */
???? if ?(?(m_pDevHandle? = ?pcap_open_live(d -> name,? // ?設備名稱
?????????????????? 65536 ,??? // ?portion?of?the?packet?to?capture.?
?????????????????? // ?65536?grants?that?the?whole?packet?will?be?captured?on?all?the?MACs.
?????????????????? 1 ,??????? // ?混雜模式
?????????????????? 1000 ,????? // ?讀超時為1秒
??????????????????m_errBuf??? // ?error?buffer
??????????????????)?)? == ?NULL)
????{
???????? /* ?釋放網絡設備? */
????????pcap_freealldevs(m_pAllDevs);
???????? return ? - 1 ;
????}
???? /* 釋放網絡設備 */
????pcap_freealldevs(m_pAllDevs);
???? return ? 1 ;
}
// ?傳入回調函數地址至此函數
void ?CNetPacket::GetPacket(pcap_back?fp,? int ?cnt)
{
????m_fp? = ?fp;
???? /* ?開始捕獲包? */
????pcap_loop(m_pDevHandle,?cnt,?packet_handler,?NULL);
}
/* ?對每一個到來的數據包調用該函數? */
void ?packet_handler(u_char? * param,? const ? struct ?pcap_pkthdr? * header,? const ?u_char? * pkt_data)
{
????m_fp(param,?header,?pkt_data);
}
??? SendPacket中使用了關鍵段保證線程安全,這樣子在使用多線程的時候就不會因為網絡設備被同時調用而出錯。SendPacket的參數包括了一個UCHAR指針及待發送數據的長度,用戶使用時把待發送的數組的地址傳入參數一,把數組長度傳入參數二。GetPacket的參數包括一個函數指針及接收數據包的數目,這里的函數指針就是用戶需要被回調的函數的地址。用戶使用的時候把函數名(即函數指針)傳入參數一,把接收的數據包的個數傳入參數二,當有數據包被檢測到的時候回調函數就會被執行,當接收的數據包的數目超出cnt時,pcap_loop就會退出(在此之前pcap_loop是沒有退出的,它是一個循環并阻塞等待數據包的函數)。
??? 由于本人是從C#轉過來的(雖然開始學的是C++,后來由于種種原因開始使用起C#),對于event方式的回調比較熟悉。開始要寫C++的回調一直沒弄明白,查了一些中文的資料覺得都看得糊里糊涂的,后來看到外國的資料才明白了
http://www.newty.de/fpt/callback.html
。其實回調的關鍵就是把需要被回調的函數的地址作為參數傳入另一個函數,還有你必須給這個函數定義一個格式,如參數和返回值,這樣子回調的時候程序才能根據地址及參數列表從內存中調用被回調的函數。當然,定義函數格式的話可以通過typedef的形式在頭文件中定義,也可以在參數列表中直接定義,如void (*p) (...)這樣。
3.寫在后面
??? 發送函數只調用了比較簡單的pcap_sendpacket,而接收也是調用了pcap_loop。可能你也發現了發送還有pcap_sendqueue_queue,接收還有pcap_next_ex,不過對于簡單的任務使用前者就足夠了。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
