CSocket类的Receive超时的问题解决方案
前幾日碰到一問題,當CSocket的Receive阻塞時,如何進行超時處理。由于程序是在多線程中使用Socket通信,開始時是在主線程中用定時監測Receive函數,當超時后,結束通信。但問題是CSocket對象無法釋放。因此從網上搜索解決辦法,直接在線程中對Receive進行超時處理。
不錯,搜到以下內容,很多網站轉載。
?為CSocket配置Time-Out功能
CSocket操作,如Send(),Receive(),Connect()都屬阻塞操作,即它們在成功完成或錯誤發生之前是不會返回的。
在某些情況下,某項操作可能永遠不能成功完成,程序為了等待其完成就得永遠循環下去。在程序中為某項操作限定一個成功完成的時間是個好主意。本文就是討論此問題的。
一個辦法是設計一個計時器,當操作費時過長時就觸發。這個辦法的關鍵是怎樣處理計時器。雖然操作是"阻塞"的,但仍具處理傳回的消息的能力。如果用SetTimer來設置計時器,就可截獲WM_TIMER消息,當它產生時就終止操作。涉及到這個過程的主要函數是:Windows API ::SetTimer(),MFC函數CSocket::OnMessagePending()和CSocket:: CancelBlockingCall()。這些功能可包裝到你的CSocket類中得以簡化。
類中用到三個重要函數:?
BOOL SetTimeOut(UINT uTimeOut)它應在CSocket函數調用前被調用。uTimeOut以千分秒為單位。下面的實現只是簡單的設置計時器。當設置計時器失敗時返回False。參見Windows API中關于SetTimer的說明。
BOOL KillTimeOut()此函數應在操作未完成被阻塞時被調用。它刪除SetTimeOut所設置的計時器。如果調用KillTimer失敗則返回False。參見Windows API中關于KillTimer的說明。
BOOL OnMessagePending()它是一個虛擬回調函數,當等待操作完成時被CSocket類調用。它給你機會來處理傳回的消息。這次我們用它來 檢查SetTimeOut所設置的計時器,如果超時(Time-Out),則它調用CancelBlockingCall()。參見MFC文檔關于OnMessagePending()和CancelBlockingCall()的說明。注意調用CancelBlockingCall()將使當前操作失敗,GetLastError()函數返回WSAEINTR(指出是中斷操作)。
下面就是使用這個類的例子: ...?
下面是示例代碼:? ?
// HEADER FILE // class CTimeOutSocket : public CSocket { public:BOOL SetTimeOut(UINT uTimeOut);BOOL KillTimeOut(); protected:virtual BOOL OnMessagePending(); private:int m_nTimerID; }; // END OF FILE // // // IMPLEMENTATION FILE // BOOL CTimeOutSocket::OnMessagePending() {MSG msg;if(::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE)) {if (msg.wParam == (UINT) m_nTimerID){// Remove the message and call CancelBlockingCall. ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);CancelBlockingCall();return FALSE; // No need for idle time processing. }}return CSocket::OnMessagePending(); } BOOL CTimeOutSocket::SetTimeOut(UINT uTimeOut) {m_nTimerID = SetTimer(NULL, 0, uTimeOut, NULL);return m_nTimerID; } BOOL CTimeOutSocket::KillTimeOut() {return KillTimer(NULL, m_nTimerID); }?
我按照以上方式建類并完成代碼。運行測試后發現并沒有實現效果!OnMessagePending沒有監測到WM_TIMER消息。
然后我對類進行了調整,一來使得封裝更好,二來外部調用基本不用做任何變化,三來希望能使OnMessagePending能夠起作用。
其實修改很簡單,就是將SetTimeOut和KillTimeOut都修改為私有函數,并重載CSocket基類的Receive和Send函數,在收發前后啟動和關閉定時器。
class CTimeOutSocket : public CSocket { // Attributes public:// Operations public:CTimeOutSocket();virtual~CTimeOutSocket();// Overrides public:// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CTimeOutSocket) public:virtual BOOL OnMessagePending();virtual int Receive(void* lpBuf, int nBufLen, int nFlags =0);virtual int Send(constvoid* lpBuf, int nBufLen, int nFlags =0);//}}AFX_VIRTUAL// Generated message map functions//{{AFX_MSG(CTimeOutSocket)// NOTE - the ClassWizard will add and remove member functions here.//}}AFX_MSG// Implementation protected:int m_nTimerID; private:BOOL KillTimeOut();BOOL SetTimeOut(int nTimeOut); };BOOL CTimeOutSocket::OnMessagePending() {MSG msg;if(::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE)){if (msg.wParam == (UINT) m_nTimerID){// Remove the message and call CancelBlockingCall. ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);CancelBlockingCall();return FALSE; // No need for idle time processing. };};return CSocket::OnMessagePending(); }int CTimeOutSocket::Receive(void* lpBuf, int nBufLen, int nFlags) {SetTimeOut(10000);int nRecv = CSocket::Receive(lpBuf, nBufLen, nFlags);KillTimeOut();return nRecv; }int CTimeOutSocket::Send(const void* lpBuf, int nBufLen, int nFlags) {SetTimeOut(10000);int nSend = CSocket::Send(lpBuf, nBufLen, nFlags);KillTimeOut();return nSend; }BOOL CTimeOutSocket::SetTimeOut(int nTimeOut) {m_nTimerID = SetTimer(NULL,0,nTimeOut,NULL);return m_nTimerID; }BOOL CTimeOutSocket::KillTimeOut() {return KillTimer(NULL,m_nTimerID); }經過修改的代碼,運行后OnMessagePending得到了WM_TIMER消息,正確解決了Receive的阻塞問題,同時我只需將原來的CSocket對象改為CTimeOutSocket對象就可以了,其它代碼不用變化。
原文地址:?http://blog.csdn.net/happyparrot/article/details/1832815
方案二:
新建線程,在線程里用 CClientSocket 實現揀消息 wm_quit (窗口關閉操作時的消息)
class CClientSocket : public CSocket
{
protected:
?? ?BOOL OnMessagePending()
?? ?{
?? ??? MSG msg;
?? ??? if (::PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE))
?? ??? {
?? ??? ?? if (IsBlocking()) CancelBlockingCall();
?? ??? ??? ?return TRUE;
?? ??? }
?? ??? return FALSE;
?? ?}
};
總結
以上是生活随笔為你收集整理的CSocket类的Receive超时的问题解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SocketAPI,CAsyncSock
- 下一篇: recv函数返回值说明