CSocket是MFC在CAsyncSocket基础上派生的一个同步阻塞Socket的封装类。它是如何又把CAsyncSocket变成同步的,而且还能响应同样的Socket事件呢?
其 实很简单,CSocket在Connect()返回WSAEWOULDBLOCK错误时,不是在OnConnect(),OnReceive()这些事件 终端函数里去等待。你先必须明白Socket事件是如何到达这些事件函数里的。这些事件处理函数是靠CSocketWnd窗口对象回调的,而窗口对象收到 来自Socket的事件,又是靠线程消息队列分发过来的。总之,Socket事件首先是作为一个消息发给CSocketWnd窗口对象,这个消息肯定需要 经过线程消息队列的分发,最终CSocketWnd窗口对象收到这些消息就调用相应的回调函数(OnConnect()等)。
所以,CSocket在调用Connect()之后,如果返回一个WSAEWOULDBLOCK错误时,它马上进入一个消息循环,就是从当前线程的消息队 列里取关心的消息,如果取到了WM_PAINT消息,则刷新窗口,如果取到的是Socket发来的消息,则根据Socket是否有操作错误码,调用相应的 回调函数(OnConnect()等)。
大致的简化代码为:
BOOL CSocket::Connect( ... )
{
if( !CAsyncSocket::Connect( ... ) )
{
if( WSAGetLastError() == WSAEWOULDBLOCK ) file://由 于异步操作需要时间,不能立即完成,所以Socket返回这个错误
{
file://进 入消息循环,以从线程消息队列里查看FD_CONNECT消息,直到收到FD_CONNECT消息,认为连接成功。
while( PumpMessages( FD_CONNECT ) );
}
}
}
BOOL CSocket::PumpMessages( UINT uEvent )
{
CWinThread* pThread = AfxGetThread();
while( bBlocking ) file://bBlocking 仅仅是一个标志,看用户是否取消对Connect()的调用
{
MSG msg;
if( PeekMessage( &msg, WM_SOCKET_NOTIFY ) )
{
if( msg.message == WM_SOCKET_NOTIFY && WSAGETSELECTEVENT(msg.lParam) == uStopFlag )
{
CAsyncSocket::DoCallBack( msg.wParam, msg.lParam );
return TRUE;
}
}
else
{
OnMessagePending(); file://处 理消息队列里的其它消息
pThread->OnIdle(-1);
}
}
}
BOOL CSocket::OnMessagePending()
{
MSG msg;
if( PeekMessage( &msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE ) )
{ file://这 里仅关心WM_PAINT消息,以处理阻塞期间的主窗口重画
::DispatchMessage( &msg );
return FALSE;
}
return FALSE;
}
其它的CSocket函数,诸如Send(),Receive(),Accept()都在收到WSAEWOULDBLOCK错误时,进入 PumpMessages()消息循环,这样一个原本异步的CAsyncSocket,到了派生类CSocket,就变成同步的了。
明白之后,我们可以对CSocket应用自如了。比如有些程序员将CSocket的操作放入一个线程,以实现多线程的异步Socket(通常,同步+多线程 相似于 异步 )。
本文来自于:http://www.chinaitpower.com/A/2001-10-12/1450.html