對CSocket及其運行機制有了較深的理解

依靠下載的Client/Server(客戶端/服務端)程序(CSocket類編寫),花了三個多小時並翻閱了MSDN文檔,並跟蹤CSocket源碼SockCore.cpp,一點體會,下文難免有不準確和疏漏之處:

客戶端與服務端的通信簡單來講:服務端socket負責監聽,應答,接收和發送消息,而客戶端socket只是連接,應答,接收,發送消息。

1. 對於Accept的block(阻塞)機制有了一定了解,調用服務端socket的Accept後,跟蹤至源碼:

while(!Accept(...))
{
    if (GetLastError() == WSAEWOULDBLOCK) //  The socket is marked as nonblocking and no connections are present to be accepted.
        PumpMessage(FD_ACCEPT);
    else
        return FALSE;
}

一個看似死循環的無數次調用socket的API函數:Accept, Accept將檢查Pending connection(來自Client端的正在處理的連接)隊列,直到檢查到一個正確的

Pending connection。消息泵函數PumpMessage將保證socket中的事件不會發生阻塞,這裏至少保證所偵聽的FD_ACCEPT事件不會阻塞。

2. 實戰之典型問題:客戶端socket要求與服務端的socket連接時,服務端的socket爲什麼會進入OnAccept消息函數?

socket創建時,根據socket的描述字,將socket分配和綁定至相應的傳輸服務供應者(個人認爲應該是windows操作系統的網絡傳輸服務進程),socket利用網絡傳輸服務進程便可獲取來自某個ip地址(客戶端)的連接請求事件,然後是提供對消息的接收和發送等其它事件。

下面進入正題,把斷點設到CAsyncSocket::AttachHandle開始處:

socket在創建時還調用CAsyncSocket::AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)(CSocket從CAsyncSocket派生),將socket實例句

柄和socket指針添加至當前模塊狀態(注1)的一個映射表變量m_pmapSocketHandle中。

接着,在AttachHandle過程中,會new一個CSocketWnd實例 - 你可以理解它是存放所有sockets的消息池(基於window消息),請仔細查看,我這裏將將sockect後多加了一個s,表示創建的多個socket將共享一個消息池CSocketWnd。

當客戶端socket要求與服務端的socket連接時, 此時socket將相應的事件通知消息WM_SOCKET_NOTIFY發送給CSocketWnd。你可能會問socket怎麼發通告消息給CSocketWnd呢,我可要先知道CSocketWnd的指針啊?答案是該指針(實際是窗口句柄)已保存在當前線程狀態的m_hSocketWindow變量中, 取之!

CSocketWnd收到事件通知消息後調用消息函數:(提示:試着在OnSocketNotify中設置斷點)

LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
 CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
 CSocket::ProcessAuxQueue();
 return 0L;
}

有點奇怪的代碼?CSocketWnd類是作爲CSocket類的友元類,這意味着它可以訪問CSocket類中的保護和私有成員函數和變量,AuxQueueAdd和ProcessAuxQueue是

CSocket類的靜態成員函數,明白了吧!還不明白?找本C++書看一下友元的使用方法吧!

PS: 消息參數wParam是socket的句柄,lParam是socket的事件。

ok!暈了吧,真的我都有點暈了,但還沒完啊,let's go on!

ProcessAuxQueue是實質處理socket通告消息的函數,在該函數中有這樣一句代碼:

CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

其實也就是由socket句柄得到發送事件通知消息的socket指針pSocket:從m_pmapSocketHandle中查找!

最後,WSAGETSELECTEVENT(lParam)會取出事件類型,在一個簡單的switch語句中判斷事件類型並調用事件處理函數。
在這裏,事件類型是FD_ACCEPT,當然就調用pSocket->OnAccept了!


注:

1. 當前模塊狀態:用於保存當前線程和模塊狀態的一個結構,可以通過AfxGetThreadModule()獲得。AFX_MODULE_THREAD_STATE在CSocket重新定義爲_AFX_SOCK_THREAD_STATE。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章