欢迎访问 生活随笔!

生活随笔

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

编程问答

CSocket类的Receive超时的问题解决方案

发布时间:2025/3/21 编程问答 49 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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(指出是中断操作)。
   下面就是使用这个类的例子: ... 

CTimeOutSocket sockServer; CAcceptedSocket sockAccept; sockServer.Create(777); sockServer.Listen(); // Note the following sequence: // SetTimeOut // <operation which might block> // KillTimeOut if(!sockServer.SetTimeOut(10000)) { ASSERT(FALSE); // Error Handling...for some reason, we could not setup // the timer. } if(!sockServer.Accept(sockAccept)) { int nError = GetLastError(); if(nError==WSAEINTR) AfxMessageBox("No Connections Arrived For 10 Seconds"); else ; // Do other error processing. } if(!sockServer.KillTimeOut()) { ASSERT(FALSE); // Error Handling...for some reason the timer could not // be destroyed...perhaps a memory overwrite has changed // m_nTimerID? // } ...

下面是示例代码:    

// 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超时的问题解决方案的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。