CSocket同步操作阻塞时设置超时的解决方案 推荐
2009-07-28 21:28
483 查看
本文讲解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
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
//错误处理
}
if(!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();
}
最近参加了中兴公司的通信软件设计大赛,开发一个模拟手机和基站信令通信的软件,遇到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
//错误处理
}
if(!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();
}
相关文章推荐
- 设置socket的Connect超时 同步 阻塞
- 使用SIGALRM信号为阻塞操作设置超时
- 使用SIGALRM信号为阻塞操作设置超时
- 如何在Phpstorm中设置FTP,并快速进行文件比较,上传下载,同步等操作
- CSocket设置超时
- SQL 超时解决方案 有时并不是设置问题
- [VC]setsockopt 设置socket 详细用法(超时、非阻塞等)
- 非阻塞同步机制与CAS操作
- 隐藏文件夹无法通过菜单工具更改设置实现显示,网络被阻塞的解决方案
- boost::asio设置同步连接超时
- android 异步回调中操作UI线程,UI同步、卡死阻塞等性能问题
- 快速笔记02-MySQL主从复制原理半同步操作步骤及原理 推荐
- CSocket超时(Time-Out)功能的设置及其局限性
- CSocket超时(Time-Out)功能的设置及其局限性
- CSocket超时(Time-Out)功能的设置及其局限性
- Python脚本控制的WebDriver 常用操作 <二十八> 超时设置和cookie操作
- connect超时设置.阻塞和非阻塞 ioctlsocket函数
- Java socket阻塞设置超时
- Winsock例子代码:初始化、阻塞/非阻塞、超时设置、select等
- qt设置QSqlQuery.exec()操作sql server时超时