日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[WinSock]封装WSAAsyncSelect!

發布時間:2024/4/11 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [WinSock]封装WSAAsyncSelect! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

封裝目標: 最終目標是封裝WinSock的WSAAsyncSelect?IO模型。

封裝原則: 耦合性[減少各種依賴,包括classes之間,編譯模塊之間。],小粒度增加可復用性。

依賴ATL/WTL。?


CKxAsyncSocket?是我要實現的class,它只需要維護SOCKET成員,另外因為是異步選擇,所以可以再維護一個HWND。

關于HWND,是否有必要暴露給用戶,關鍵在于Socket要僅僅運行于UI主線程,還是用戶可以自己創建另一個有消息循環的Thread,在其上處理Socket的消息。

我選擇后者,因為,盡管異步的Socket,在Recv大數據的時候,還是可能導致UI“卡”的。

?

那么,我們就需要一個線程,一個有處理消息的窗口,二者形成一個UI線程, 然后再該UI線程處理Socket消息。以上提到的幾個實體,實體之間要盡量低耦合,無依賴。

【通常來說,接口耦合和可以接受的,我們也會通過一些技巧來實現低耦合】

1. 線程只需要一個簡單的CreateThread封裝,Like Java Style的就好,支持Runnable接口,好處就不說了。

2. 處理消息的窗口,我們用WTL,搞個簡單的:

#define?CHAIN_MSG_MAP_MEMBER_PTR(theChainMemberPtr)?\
????{?\
????????if(?NULL?!=?theChainMemberPtr?&&?\
????????????theChainMemberPtr->ProcessWindowMessage(hWnd,?uMsg,?wParam,?lParam,?lResult))?\
????????????return?TRUE;?\
????}
????
class?MessageOnlyWindow?:?public?CWindowImpl<MessageOnlyWindow,?CWindow,?CWinTraits<>?>
{
????public:
????????BEGIN_MSG_MAP(MessageOnlyWindow)
????????????CHAIN_MSG_MAP_MEMBER_PTR(?m_pWinMsgHandler?)
????????END_MSG_MAP()

????????MessageOnlyWindow(){}

????????~MessageOnlyWindow()
????????{
????????????::DestroyWindow(m_hWnd);
????????????m_hWnd?=?NULL;
????????}

????????HWND?Create(ProcessWindowMessageHandler*?pWinMsgHandler)
????????{
????????????m_pWinMsgHandler?=?pWinMsgHandler;
????????????return?CWindowImpl<MessageOnlyWindow,?CWindow,?CWinTraits<>?>::Create(HWND_MESSAGE);
????????}
????private:
????????ProcessWindowMessageHandler*?m_pWinMsgHandler;

};??

需要說明一下:

CHAIN_MSG_MAP_MEMBER_PTR是仿WTL的宏,WTL缺少一個處理成員指針的CHAIN*. 這樣 HWND_MESSAGE窗口的消息,都可以投遞給 ProcessWindowMessageHandler*?m_pWinMsgHandler了。

?

PS:

class?__declspec(novtable)?ProcessWindowMessageHandler
{
public:
????virtual?BOOL?ProcessWindowMessage(HWND?hWnd,?UINT?uMsg,?WPARAM?wParam,?LPARAM?lParam,?LRESULT&?lResult,?DWORD?dwMsgMapID?=?0)?=?0;
};
//?ProcessWindowMessage和BEGIN_MSG_MAP的聲明是一致的,但是處理成虛函數了。這樣可以多態到派生類,但是又不會依賴派生類(的聲明)
//?WTL大量依賴模板來處理這種關系。?

那么現在,我們再創建一個類,表達UI Thread的概念即可,它可以繼承或者聚合一個CKxThread即可。然后在線程中創建窗口和消息循環即可:

virtual?int?Run(?CKxThread*?pThread?)?

{
????MessageOnlyWindow?msgWnd;
????HWND?hWnd?=?msgWnd.Create(m_pRoutine);

????m_pRoutine->OnBegin(?pThread,?hWnd?);

????MSG?msg;
????while(?GetMessage(&msg,?NULL,?0,?0)?)
????{
????????TranslateMessage(?&msg?);
????????DispatchMessage(?&msg?);
????}
????return?0;
}?// 代碼有節略.

再看下m_pRoutine的類型:

?

class?__declspec(novtable)?IKxMessageRoutine?:?public?ProcessWindowMessageHandler

?

實際上用戶自己派生的

IKxMessageRoutine ,就可以通過Windows Message Thread實例傳遞給 MessageOnlyWindow 。這樣用戶的派生類就可以直接Handle這個Thread的消息了,看一下這個派生類的樣子:

?

class?CUserThreadMessageHandler?:?public?IKxMessageRoutine,?public?CKxAsyncSocketMessageMixin
{
private:
????BEGIN_MSG_MAP( CUserThreadMessageHandler)
????????CHAIN_MSG_MAP(CKxAsyncSocketMessageMixin)

? ? END_MSG_MAP()??

首先,我們可以確定的是,

CUserThreadMessageHandler 僅僅是消息的處理者,而且和Socket事件沒有必然聯系,如果不繼承 CKxAsyncSocketMessageMixin (忽略此處的Public繼承)的話。因為Windows中,除了異步選擇的Socket IO模型,還有很多依賴消息循環的編程模型。所以不考慮第二個繼承關系的 CUserThreadMessageHandler 是一個很好的處理UI 線程消息的對象。在該線程中,需要處理的消息,可以按照WTL的消息映射寫處理函數。

?

那么,我們現在要處理Socket的事件了,好,加一個Mixin即可【當然了,要通過

CHAIN_MSG_MAP 來把CKxAsyncSocketMessageMixin接入到消息隊列中】,這樣,一個Socket綁定了這個線程的消息窗口后,就可以在這個線程的消息循環中工作了。

?


CKxAsyncSocketMessageMixin是一個處理Socket事件的類,并且把SOCKET<->Socket對象指針進行映射【沒辦法,OS投遞SOcket事件的時候,參數是SOCKET,而不是別的,所以自己要做這個Mapping,搞過WinSock封裝的人,都清楚這點。HWND<->CWnd*的映射也是一般道理?!?/p> class?CKxAsyncSocketMessageMixin?

{
public:
????
????BEGIN_MSG_MAP(CKxAsyncSocketMessageMixin)
????????MESSAGE_HANDLER(HK_WM_SOCKET,?OnSocketMessage)
...?...

//?HK_WM_SOCKET就是WinSock?API?WSAAsyncSelect的第三個參數,你懂的。?

最后,Socket對象也就可以收到消息了。然后根據消息轉到不同的處理函數即可,OnConnect,OnRecv...[并且我的Socket,也實行了ProcessWindowMessageHandler接口]


-------------------------------------------------------------------------------------------------------------------------------------------------------

// Socket 本身相關的.?

[WinSock]其實和WTL關系不大,但是我會用到一些WTL的東西。

?

因為是異步選擇,所以需要一個窗口來接收消息,于是...

class?MessageOnlyWindow?:?public?CWindowImpl<MessageOnlyWindow,?CWindow,?CWinTraits<>?>
{
????BEGIN_MSG_MAP(MessageOnlyWindow)
????MESSAGE_HANDLER(WM_SOCKET,?OnSocketMessage)
????END_MSG_MAP()

? ? HWND Create()

{ return CWindowImpl<MessageOnlyWindow, CWindow, CWinTraits<> >::Create(HWND_MESSAGE);

?}?

};

這樣,非常簡單的搞定了消息窗口【可算是和WTL扯上關系了】


異步Socket的Receive才是我關注的:

[1]? ? ? ?

WSAAsyncSelect自動把一個阻塞的socket轉為非阻塞的,如果需要轉為阻塞的,那么先要調用這個函數,并且(最后一個參數long lEvent設置為0) 。然后調用ioctlsocket,或者WSAIoctl。?

// MSDN:The?WSAAsyncSelect?function automatically sets socket?s?to?nonblocking?mode, regardless of the value of?lEvent. To set socket?s?back to blocking mode, it is first?necessary?to clear the event record associated with socket?s?via a call to?WSAAsyncSelect?with?lEvent?set to?zero. You can then call?ioctlsocket?or?WSAIoctl?to set the socket back to blocking mode.

設置為阻塞的:

?

u_long?iMode?=?0;????//?set?blocking.
????ioctlsocket(?m_hSocket,?FIONBIO,?&iMode?);

?


[2]?

一旦設置了阻塞,我想,還是加個超時的好:

?

DWORD?dwRecvTimeout?=?5000;?//?5?sec?timeout;
? ? setsockopt(?m_hSocket,?SOL_SOCKET,?SO_RCVTIMEO,?(char*)&dwRecvTimeout,?sizeof(DWORD)?);

?

總結

以上是生活随笔為你收集整理的[WinSock]封装WSAAsyncSelect!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。