天天看點

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 版本。

    相關函數如下:

     調用此函數之後僅接着調用 CSocket 函數(如 Receive、Send 和 Accept)。uTimeOut 參數是以毫秒為機關指定的。之後,進行定時器的設定。如果設定定時器失敗,那麼函數傳回 FALSE。有關詳細情況,請參閱 SetTimer 函數的 Windows API 文檔。

     在完成阻塞操作後,必須調用此函數。此函數删除用 SetTimeOut 設定的定時器。如果調用 KillTimer 失敗,則傳回 FALSE。有關詳細情況,請參閱 KillTimer 函數的 Windows API 文檔。 

     這是一個虛拟回調函數,在等待操作完成時由 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

  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

             //錯誤處理

if(!sockServer.KillTimeOut())

    //不能取消定時,錯誤處理

網上的另一種實作:

// 自己計算時間的辦法 OK

         BOOL SetTimeOut(UINT uTimeOut=1000);

         BOOL KillTimeOut();

    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,如需轉載請自行聯系原作者

繼續閱讀