CSocket同步操作阻塞时设置超时的解决方案

简介:
    本文讲解CSocket同步操作阻塞时设置超时的解决方案。
    最近参加了中兴公司的通信软件设计大赛,开发一个模拟手机和基站信令通信的软件,遇到CSocket发送阻塞的问题,这里有一个简单的解决方案供大家参考。
    CSocket继承自CAsyncSocket,他们的不同是前者是同步套接字,后者是异步套接字,操作都是异步的。Socket中的Receive、Send 和Connect是阻塞操作,我们知道阻塞操作的特点是要么运行成功退出,要么出现错误退出,而且有的时候如果不成功,则该函数会一直阻塞,整个程序会一直等待操作的完成。
    本文提出的解决方法是通过编程限制完成操作使用的时间,设置定时,设定一定的超时时间,如果阻塞操作时间过程,则启动该超时机制。虽然操作是阻塞操作,但是我们可以处理操作中到达的消息。如果通过使用 SetTimer 设置定时器,那么可以查找 WM_TIMER 消息,并在收到该消息时终止操作。
    这种方法比较常见,也比较容易想到,网上也有很多相关的思想。设置定时的方法有::SetTimer函数,具体参加msdn。处理消息过程是CSocket中的CSocket::OnMessagePending 函数,退出阻塞的函数是CSocket中的CSocket::CancelBlockingCall 函数。我们在使用的时候往往是定义自己的CClientSocket类继承自CSocket类,这样这些函数都可以获得了。
    本人通过网上的资料得知,这种方法目前仅适用于 Visual C++ 的 1.52、1.52b、2.1 和 2.2 版本。
    相关函数如下:

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(表示操作中断)。 

    相关实现如下:
头文件
class CClientSocket :  public CSocket
{
   //friend CClientThread;
// Attributes
public:
   //CMCSServerDlg* m_dlgServer;
   //CWinThread* m_pThread;

private:
   int m_nTimerID;
// Overrides
public:
  BOOL KillTimeOut();
  BOOL SetTimeOut(UINT uTimeOut);
   // ClassWizard generated virtual function overrides
   //{{AFX_VIRTUAL(CClientSocket)
   public:
   virtual  void OnReceive( int nErrorCode);
   virtual  void OnClose( int nErrorCode);
   virtual BOOL OnMessagePending();
   //}}AFX_VIRTUAL

//设置超时
BOOL CClientSocket::SetTimeOut(UINT uTimeOut)
{
  m_nTimerID = SetTimer(NULL,0,uTimeOut,NULL);
   return m_nTimerID;
}

//取消设置超时
BOOL CClientSocket::KillTimeOut()
{
   return KillTimer(NULL,m_nTimerID);
}

//用于CSocket函数阻塞时,如果超时,则退出该阻塞函数
BOOL CClientSocket::OnMessagePending() 
{
  MSG msg;
   if(::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE)) {
     if (msg.wParam == (UINT) m_nTimerID) {
      ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
      CancelBlockingCall();
       return FALSE; 
    };
  };

   return CSocket::OnMessagePending();
}

    使用这种机制的代码如下:
if(!sockServer.SetTimeOut(10000))
{
      // 错误处理,不能建立定时
 }
if(!sockServer.Accept(sockAccept))
 {
        int nError = GetLastError();
        if(nError==WSAEINTR)
              //重传处理等等
         else
              //错误处理
 }
i f(!sockServer.KillTimeOut())
 {
     //不能取消定时,错误处理
 }


网上的另一种实现:
// 自己计算时间的办法 OK
public:
         BOOL SetTimeOut(UINT uTimeOut=1000);
         BOOL KillTimeOut();
private:
    LONGLONG m_llDtStart;
    UINT    m_uTimeOut; 
};

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

//`AFX_INSERT_LOCATION`
// 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(CTimeOutSock)
  //}}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();
}


     本文转自panpan3210 51CTO博客,原文链接:http://blog.51cto.com/panpan/184531,如需转载请自行联系原作者







相关文章
|
6月前
|
存储 网络协议 iOS开发
connect永远阻塞线程及解决方案
connect永远阻塞线程及解决方案
107 0
|
6月前
线程间的同步的方式有哪些
线程间的同步的方式有哪些
|
Java 测试技术
高并发编程-自定义带有超时功能的锁
高并发编程-自定义带有超时功能的锁
68 0
|
调度 C++
进程、线程、并发、并行、同步、异步、阻塞、非阻塞
乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
阻塞/阻塞/同步/异步
咱就说有没有一种可能,同步、异步、阻塞、非阻塞,这几个关键词拆开看都感觉挺明白的。但是同步阻塞、同步非阻塞、异步阻塞、异步非阻塞,这几个关键词组装起来,看起来就有点那么晦涩了。这个在日常八股中经常出现字眼,其背后对应的到底是个什么样的逻辑?我们来一起揭开它那不那么神秘的面纱。/手动狗头。
119 0
阻塞/阻塞/同步/异步
currentHashMap的公平锁,可中断响应,限制等待实例
currentHashMap的公平锁,可中断响应,限制等待实例
currentHashMap的公平锁,可中断响应,限制等待实例
|
调度
线程的状态及阻塞
之前提到了线程和进程,那就提一下线程的状态吧!
241 0
轮询锁使用时遇到的问题与解决方案!(4)
轮询锁使用时遇到的问题与解决方案!(4)
119 0
轮询锁使用时遇到的问题与解决方案!(4)
轮询锁使用时遇到的问题与解决方案!(3)
轮询锁使用时遇到的问题与解决方案!(3)
99 0
轮询锁使用时遇到的问题与解决方案!(3)
轮询锁使用时遇到的问题与解决方案!(7)
轮询锁使用时遇到的问题与解决方案!(7)
100 0
轮询锁使用时遇到的问题与解决方案!(7)