天天看點

MFC之旅——CSocket神級易錯點易錯點一易錯點二

CSocket是對底層winsock32的api的封裝,實作對象型。友善我們的網絡MFC程式的編寫。

這個類的使用大家很可能會忽視一點。導緻一個很難受的報錯。

我的經曆就是兩個字:難受。

易錯點一

寫MFC網絡程式時,一般都是需要使用多線程的。我在無聊編寫一個聊天軟體時。就出現了這樣的一個報錯。(VS2017 & win7 64)

BOOL CSocket::PumpMessages(UINT uStopFlag)
{
	// The same socket better not be blocking in more than one place.
	ASSERT(m_pbBlocking == NULL);

	_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;

	ASSERT(pState->m_hSocketWindow != NULL);

	BOOL bBlocking = TRUE;
	m_pbBlocking = &bBlocking;
	CWinThread* pThread = AfxGetThread();
           

在這裡會引發一個斷點。但是把代碼放在我的主線程下運作就不會有問題。

引起這個斷點的錯誤原因,多半就是,你并沒有在這個建立立的線程裡面調用AfxSocketInit()初始化socket....

這個錯誤我在學習MFC的socket時,在MFC中看到過。但是,在實際編寫過程中,我還是犯錯了。是以這說明,你學過的知識,沒有運用。你是不會正真的記住。

易錯點二

在進行多線程網絡程式設計時,難免通信的處理要轉移給另一個線程去執行。我很自然的會想到直接把。這個CSocket對象位址傳給另一個線程去調用,不就可以了?然而,實際上我這樣寫了。卻出現了錯誤。後來經過網上查找資料,找到了這樣一篇部落格。

下面附上連結:跨線程使用CSocket

這位作者詳細的講述了他尋找原因的過程,希望對大家有幫助,我的了解是,為了保護線程的網絡通信安全。于是MFC内部的CSocket會在一個CSocket建立後。将目前線程ID存儲在一個成員變量内部,當然,應該是非public。在其他線程中調用此CSocket的成員函數時,驗證線程ID,如果不同,就會抛出異常。

Remark:這裡需要注意的是,在我實際編寫中發現,并不是所有的成員函數都會進行驗證。比如我調用SendTo()這個函數就算是跨線程也能正常執行。但是ReceiveFrom()就不行了。這就很奇怪了。為什麼隻保護接收,而不保護發送。

跨線程時的解決辦(引用自上面那篇部落格):既然CSocket綁定在了線程上,那麼就幫CSocket更換一個綁定對象。在原線程中調用其成員函數SOCKET CSocket::Detach();解除對象與SOCKET的綁定。然後把SOCKET傳入另一個線程中,在其中調用BOOL CSocket::Attach(SOCKET socket)進行綁定。這樣就把這個SOCKET綁定到這屬于這個線程的一個CSocket對象上了。

不知道大家發現沒,這個非常像互斥對象。一個線程在使用時,其他線程就無法調用。必須讓原線程調用Detach放棄使用權。另一個線程才能Attach擷取使用權。是以,如果這個線程不用了,要把使用權給其他線程時,也要記得調用Detach哦,這是在編寫程式中特别要注意的。不然,BUG會帶走你的快樂哦!