配置Csocket 操作的超时时间
最后修改 : 2005824 
本文的发布号曾为 CHS138692
转载自www.blogjava/weidagang2046/articles/79285.html
BOOL SetTimeOut(UINT uTimeOut)
BOOL KillTimeOut()
BOOL OnMessagePending()
示例代码
参考
概要
CSocket 操作,如接收”(Receive)发送”(Send) 连接”(Connect) 均是阻塞操作,即要等到操作成功执行完毕或套接字上出现错误后,对这些函数的调用才有返回结果。
在某些情况下,操作可能永远不能成功完成,这将导致程序无限循环等待操作完成。一种解决方法是通过编程限制完成操作使用的时间。本文将讨论这种方法。 
 
这种方法是设置定时,让它在操作时间过长时启动。此方法的关键在于处理定时器的方式。虽然操作是阻塞的,但仍然可以处理到达的消息。如果通过使用 SetTimer 设置定时器,那么可以查 WM_TIMER 消息,并在收到该消息时终止操作。该过程中涉及的主要函数有: 
Windows API 调用函数: 
::SetTimer 
MFC 函数: 
CSocket::OnMessagePending 
CSocket::CancelBlockingCall 
为简单起见,可以在 Csocket 衍生类中封装该功能。
警告:在进一步阅读本文之前,请注意在某些 MFC 版本中存在错误,会在试图使用定时
器并重叠 OnMessagePending 时引起问题。这一问题将在下面的 Microsoft Knowledge Base 文章中进行讨论: 
137632 (support.microsoft/kb/137632/EN-US/) 错误:定时器激活时未调用 OnMessagePending 
本文仅适用于 Visual C++ 1.521.52b2.1 2.2 版本。如果使用的是这些 Visual C++ 版本之一,则还需要实施所提供的变通解决方法。
本文最后部分显示提供这种超时功能的类的示例代码。以下内容讲述由该类实现的函数。 
BOOL SetTimeOut(UINT uTimeOut)

调用此函数之后紧接着调用 CSocket 函数(如 ReceiveSend Accept)。uTimeOut 参数是以毫秒为单位指定的。之后,进行定时器的设置。如果设置定时器失败,那么函数返回 FALSE。有关详细情况,请参阅 SetTimer 函数的 Windows API 文档。 
 BOOL KillTimeOut()


在完成阻塞操作后,必须调用此函数。此函数删除用 SetTimeOut 设置的定时器。如果调用 KillTimer 失败,则返回 FALSE。有关详细情况,请参阅 KillTimer 函数的 Windows API 文档。 
BOOL OnMessagePending()

这是一个虚拟回调函数,在等待操作完成时由 CSocket 类进行调用。此函数给您提供处理传入消息的机会。此实施过程检查用 SetTimeOut 调用函数设置的定时器的 WM_TIMER 消息。如果收到消息,则调用 CancelBlockingCall 函数。有关 OnMessagePending CancelBlockingCall 函数详细的信息,请参阅 MFC 文档。请注意:调用 CancelBlockingCall 函数 将导致操作失败,而且 GetLastError 函数返回 WSAEINTR(表示操作中断)。
下面是使用该类的一个例子: 
  ...
  CTimeOutSocket sockServer;
  CAcceptedSocket sockAccept;
  sockServer.Create(777);
  sockServer.Listen();
  // Note the following sequence:
  //  SetTimeOut
  //  
  //  KillTimeOut
  if(!sockServer.SetTimeOut(10000))
timeout on t2 timer  {
    ASSERT(FALSE);
    // 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);
    // for some reason the timer could not
    // 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);
  }
CSOCKET的超时设置和UDP发送接收
    使用CSoket多次了,但对于它的block模式的理解并不是很深入。昨天使用csoketudp多发测试(server接到数据后,需要通过某种方式将数据发送到client,使用tcp方式比较可靠,我一直这样用的,但是比较费时,需要逐一发送),发现了问题:
  1create(),sendto(),receivefrom()....
  2)其中,发送方一直定时发送数据无问题;
  3)而接收方,通过一个单独的接收线程实现( 注意:csocket不能跨线程使用!主线程中socket create()后,detach()并将sock作为lpvoid传入接收线程 )。代码如下:
  //处理接收数据
UINT CSockSvr::DealSvrRevData(LPVOID lParam)
{
  ......
 DWORD dwError;
 TCHAR cBuff[1000];
 CString sIP;
 UINT uPort;
 for(;!pDlg->m_bExit;)
 { 
  ::memset( cBuff,0,sizeof(cBuff) );
  // 如果没有接到数据,一直等待。。。。
  // 阻塞模式的弊端:::在退出时候,通过CancelBlockingCall
  int iRst=SockSvr.ReceiveFrom( cBuff,sizeof(cBuff),sIP,uPort,0 );
    if( iRst!=SOCKET_ERROR )
  {
  CString sTemp=cBuff;
  TRACE1( _T("\r\n Rev Data: %s\r\n"),sTemp );
  }
  else
  {
  dwError=GetLastError();
  TRACE1( _T("\r\n Rev Data Error code: %d\r\n"),dwError );
  }
  ::Sleep(200);
 }
 
 return 0;
}
  问题:如果发送方没有数据,SockSvr.ReceiveFrom()会一致处于等待状态,导致整个处理接收线程的停止,如果用户需要退出/结束程序,无法正确释放资源(无法关闭在线程函数重打开的socket--不能跨线程操作socket,无法关闭线程),造成系统

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。