前几日碰到一问题,当CSocket的Receive阻塞时,如何进行超时处理。由于程序是在多线程中使用Socket通信,开始时是在主线程中用定时监测Receive函数,当超时后,结束通信。但问题是CSocket对象无法释放。因此从网上搜索解决办法,直接在线程中对Receive进行超时处理。
不错,搜到以下内容,很多网站转载。
为CSocket配置Time-Out功能
CSocket操作,如Send(),Receive(),Connect()都属阻塞操作,即它们在成功完成或错误发生之前是不会返回的。 在某些情况下,某项操作可能永远不能成功完成,程序为了等待其完成就得永远循环下去。在程序中为某项操作限定一个成功完成的时间是个好主意。本文就是讨论此问题的。 一个办法是设计一个计时器,当操作费时过长时就触发。这个办法的关键是怎样处理计时器。虽然操作是"阻塞"的,但仍具处理传回的消息的能力。如果用SetTimer来设置计时器,就可截获WM_TIMER消息,当它产生时就终止操作。涉及到这个过程的主要函数是:Windows API ::SetTimer(),MFC函数CSocket::OnMessagePending()和CSocket:: CancelBlockingCall()。这些功能可包装到你的CSocket类中得以简化。 类中用到三个重要函数: BOOL SetTimeOut(UINT uTimeOut) 它应在CSocket函数调用前被调用。uTimeOut以千分秒为单位。下面的实现只是简单的设置计时器。当设置计时器失败时返回False。参见Windows API中关于SetTimer的说明。 BOOL KillTimeOut() 此函数应在操作未完成被阻塞时被调用。它删除SetTimeOut所设置的计时器。如果调用KillTimer失败则返回False。参见Windows API中关于KillTimer的说明。 BOOL OnMessagePending() 它是一个虚拟回调函数,当等待操作完成时被CSocket类调用。它给你机会来处理传回的消息。这次我们用它来检查SetTimeOut所设置的计时器,如果超时(Time-Out),则它调用CancelBlockingCall()。参见MFC文档关于OnMessagePending()和CancelBlockingCall()的说明。注意调用CancelBlockingCall()将使当前操作失败,GetLastError()函数返回WSAEINTR(指出是中断操作)。 下面就是使用这个类的例子:
...
CTimeOutSocket sockServer;
CAcceptedSocket sockAccept;
sockServer.Create(777);
sockServer.Listen();
// Note the following sequence:
// SetTimeOut
// <operation which might block>
// 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);
}
|
我按照以上方式建类并完成代码。运行测试后发现并没有实现效果!OnMessagePending没有监测到WM_TIMER消息。
然后我对类进行了调整,一来使得封装更好,二来外部调用基本不用做任何变化,三来希望能使OnMessagePending能够起作用。
其实修改很简单,就是将SetTimeOut和KillTimeOut都修改为私有函数,并重载CSocket基类的Receive和Send函数,在收发前后启动和关闭定时器。
class CTimeOutSocket : public CSocket
{
// Attributes
public:
// Operations
public:
CTimeOutSocket();
virtual ~CTimeOutSocket();
// Overrides
public:
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTimeOutSocket)
public:
virtual BOOL OnMessagePending();
virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0);
virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);
//}}AFX_VIRTUAL
// Generated message map functions
//{{AFX_MSG(CTimeOutSocket)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
// Implementation
protected:
int m_nTimerID;
private:
BOOL KillTimeOut();
BOOL SetTimeOut(int nTimeOut);
};
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();
}
int CTimeOutSocket::Receive(void* lpBuf, int nBufLen, int nFlags)
{
SetTimeOut(10000);
int nRecv = CSocket::Receive(lpBuf, nBufLen, nFlags);
KillTimeOut();
return nRecv;
}
int CTimeOutSocket::Send(const void* lpBuf, int nBufLen, int nFlags)
{
SetTimeOut(10000);
int nSend = CSocket::Send(lpBuf, nBufLen, nFlags);
KillTimeOut();
return nSend;
}
BOOL CTimeOutSocket::SetTimeOut(int nTimeOut)
{
m_nTimerID = SetTimer(NULL,0,nTimeOut,NULL);
return m_nTimerID;
}
BOOL CTimeOutSocket::KillTimeOut()
{
return KillTimer(NULL,m_nTimerID);
}
经过修改的代码,运行后OnMessagePending得到了WM_TIMER消息,正确解决了Receive的阻塞问题,同时我只需将原来的CSocket对象改为CTimeOutSocket对象就可以了,其它代码不用变化。
分享到:
相关推荐
配置CSocket操作的超时时间.doc
利用MFC的Csocket类实现网络通信
继承Csocket类编写自己的socket类,实现多人聊天的聊天室程序,分为服务器端和客户端。
通过对CSocket类的改造和封装,轻松实现服务器端的连接侦听、管理以及客户端的连接请求、信息发送。
使用CSocket类传输文件
vc++编写的,由CSocket类派生出了CDataSocket类进行通信,使用了串行化和多线程来辅助完成该聊天程序。
利用MFC的CSocket类实现网络聊天程序,基于tcp/ip协议
基于CSocket类的简易聊天室,代码不多,有助于看清真个CSocket通讯的程序结构
这是由MFC的Csocket类实现得聊天通信程序,还算可以。
利用Visual C++的CSocket类实现局域网聊天
VC2005 写的 通过UDP 实现 通讯。需要研究网络通讯的同学可以下载学习一下
很好的学习MFC中网络编程CSocket类的程序,程序中有服务器端和客户端。
ppt写的较为详细,代码不是很突出。侧重过程,新手可以借鉴下
套接字和CSocket类套接字和CSocket类套接字和CSocket类套接字和CSocket类套接字和CSocket类
使用CSocket类的网络通信实例
利用MFC的Csocket类实现C/S网络通信
CSocket类数据接收存在的问题及Win32函数解决办法
根据课本写的MFC CSocket类写的 基本聊天室程序。 服务端与多个客户端连接,实现聊天功能。
1.用VC++6.0编程,CSocket类 2.TCP网络通讯,在线聊天室 3.无阻塞的消息响应函数 4.可以发送,接收消息。 5.如果本机网络无连接,服务器端和客户端同事在本机运行时,IP请填写127.0.0.1