weidagang2046的专栏

物格而后知致
随笔 - 8, 文章 - 409, 评论 - 101, 引用 - 0
数据加载中……

配置Csocket 操作的超时时间

察看本文应用于的产品
文章编号 : 138692
最后修改 : 2005年8月24日
修订 : 1.1
本文的发布号曾为 CHS138692
本页

概要

更多信息

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 (http://support.microsoft.com/kb/137632/EN-US/) 错误:定时器激活时未调用 OnMessagePending
本文仅适用于 Visual C++ 的 1.52、1.52b、2.1 和 2.2 版本。如果使用的是这些 Visual C++ 版本之一,则还需要实施所提供的变通解决方法。

本文最后部分显示提供这种超时功能的类的示例代码。以下内容讲述由该类实现的函数。
 回到顶端

BOOL SetTimeOut(UINT uTimeOut)


调用此函数之后仅接着调用 CSocket 函数(如 Receive、Send 和 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))
   {
     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);
   }
CSOCKET的超时设置和UDP发送接收
    使用CSoket多次了,但对于它的block模式的理解并不是很深入。昨天使用csoket的udp多发测试(server接到数据后,需要通过某种方式将数据发送到client,使用tcp方式比较可靠,我一直这样用的,但是比较费时,需要逐一发送),发现了问题:

  1)create(),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,无法关闭线程),造成系统memory leak...

  针对这种问题,微软的解决办法是:http://support.microsoft.com/default.aspx?scid=kb;zh-cn;138692 ,经过测试,在socket进行connect()的时候不好用。后来,结合微软的办法,我通过自定义的定时器,实现了block超时间后自动退出的功能〉

  #if !defined(AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_)
#define AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TimeOutSock.h : header file
//

/*
CSocket 操作,如接收(Receive)、发送(Send) 和连接(Connect) 均是阻塞操作,
 即要等到操作成功执行完毕或套接字上出现错误后,对这些函数的调用才有返回结果。
某些情况下,操作可能永远不能成功完成,这将导致程序无限循环等待操作完成。
 一种解决方法是通过编程限制完成操作使用的时间。本文将讨论这种方法。
*/
/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock command target

class CTimeOutSock : public CSocket
{
// Attributes
public:

// Operations
public:
 CTimeOutSock();
 virtual ~CTimeOutSock();

// Overrides
public:
 // ClassWizard generated virtual function overrides
 //{{AFX_VIRTUAL(CTimeOutSock)
 public:
 virtual BOOL OnMessagePending();
 //}}AFX_VIRTUAL

 // Generated message map functions
 //{{AFX_MSG(CTimeOutSock)
  // NOTE - the ClassWizard will add and remove member functions here.
 //}}AFX_MSG

/*
//定时器设置block超时 不是很好用。。。徐卫话 2006.4.19
CSocket 操作,如接收(Receive)、发送(Send) 和连接(Connect) 均是阻塞操作,
 即要等到操作成功执行完毕或套接字上出现错误后,对这些函数的调用才有返回结果。
某些情况下,操作可能永远不能成功完成,这将导致程序无限循环等待操作完成。
 一种解决方法是通过编程限制完成操作使用的时间。本文将讨论这种方法。
*/
// 自己计算时间的办法 OK
public:
     BOOL SetTimeOut(UINT uTimeOut=1000);
     BOOL KillTimeOut();
private:
  LONGLONG m_llDtStart;
  UINT  m_uTimeOut;
};

/////////////////////////////////////////////////////////////////////////////

//}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_TIMEOUTSOCK_H__19897A81_4EAF_4005_91FD_DC3047725139__INCLUDED_)


  // TimeOutSock.cpp : implementation file
//

#include "stdafx.h"
#include "NetBroad.h"
#include "TimeOutSock.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock

CTimeOutSock::CTimeOutSock()
{

}

CTimeOutSock::~CTimeOutSock()


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CTimeOutSock, CSocket)
 //}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0

/////////////////////////////////////////////////////////////////////////////
// CTimeOutSock member functions

//设置超时
BOOL CTimeOutSock::SetTimeOut(UINT uTimeOut)
{  
 //get start cnt
 LARGE_INTEGER llCnt;
 ::QueryPerformanceCounter(&llCnt);
 m_llDtStart=llCnt.QuadPart;
 m_uTimeOut=uTimeOut;

     return TRUE;
}

//删除超时参数
BOOL CTimeOutSock::KillTimeOut()
{
 m_llDtStart=0;//表明取消计时
 return TRUE;
}

//检查是否超时间
BOOL CTimeOutSock::OnMessagePending()
{
 // TODO: Add your specialized code here and/or call the base class
 /*
 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.
   };
  };
 */
 if( m_llDtStart )
 {
  LARGE_INTEGER lldtEnd;
  ::QueryPerformanceCounter(&lldtEnd); 
  LARGE_INTEGER llFrq;
  ::QueryPerformanceFrequency(&llFrq);
  double dbDealy=(double)(lldtEnd.QuadPart-m_llDtStart)*1000/llFrq.QuadPart;
  if( dbDealy>m_uTimeOut )
  {
   CancelBlockingCall();
   return FALSE;  // No need for idle time processing.
  }
 }
 
 return CSocket::OnMessagePending();
}

 

  经过改进后, 对处理接收线成的函数进行了部分改进:

  SockSvr.SetTimeOut(500);  int iRst=SockSvr.ReceiveFrom( cBuff,sizeofcBuff),sIP,uPort,0 );  SockSvr.KillTimeOut();

 当block超过定时后,socket自动退出block,防止接收线程停止。问题终于解决了!

from: http://www.arm8.com/cv/1/6/197.html

posted on 2006-11-06 00:16 weidagang2046 阅读(5012) 评论(1)  编辑  收藏 所属分类: Windows

评论

# Seoclick  回复  更多评论   

Greeting. Seize opportunity by the beard, for it is bald behind.
I am from Cyprus and , too, and now am writing in English, tell me right I wrote the following sentence: "While obviously not meant in any serious manner, the joke reminds me of my attitude towards seo, or “search engine optimization”.Offers search engine optimisation, web promotion and internet marketing services."

:-( Thanks in advance. Grania.
2009-05-18 20:44 | Seoclick

只有注册用户登录后才能发表评论。


网站导航: