WSAAsyncSelect( m_hSocket, pSockWnd->m_hWnd, WM_SOCKET_NOTIFY, lEvent ); //Socket事件和窗口關(guān)聯(lián)
}
static void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket Socket;
Socket.Attach( (SOCKET)wParam ); //wParam就是觸發(fā)這個(gè)事件的Socket的句柄
int nErrorCode = WSAGETSELECTERROR(lParam); //lParam是錯誤碼與事件碼的合成
switch (WSAGETSELECTEVENT(lParam))
{
case FD_READ:
pSocket->OnReceive(nErrorCode);
break;
case FD_WRITE:
pSocket->OnSend(nErrorCode);
break;
case FD_OOB:
pSocket->OnOutOfBandData(nErrorCode);
break;
case FD_ACCEPT:
pSocket->OnAccept(nErrorCode);
break;
case FD_CONNECT:
pSocket->OnConnect(nErrorCode);
break;
case FD_CLOSE:
pSocket->OnClose(nErrorCode);
break;
}
}
CSocketWnd類(lèi)大致為:
BEGIN_MESSAGE_MAP(CSocketWnd, CWnd)
ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify)
END_MESSAGE_MAP()
LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket::DoCallBack( wParam, lParam ); //收到Socket事件消息,回調CAsyncSocket的DoCallBack()函數
return 0L;
}
然而,最不容易被初學(xué)Socket編程的人理解的,也是本文最要提醒的一點(diǎn)是,客戶(hù)方在使用CAsyncSocket::Connect()時(shí),往往返回一個(gè)WSAEWOULDBLOCK的錯誤(其它的某些函數調用也如此),實(shí)際上這不應該算作一個(gè)錯誤,它是Socket提醒我們,由于你使用了非阻塞Socket方式,所以(連接)操作需要時(shí)間,不能瞬間建立。既然如此,我們可以等待呀,等它連接成功為止,于是許多程序員就在調用Connect()之后,Sleep(0),然后不停地用WSAGetLastError()或者CAsyncSocket::GetLastError()查看Socket返回的錯誤,直到返回成功為止。這是一種錯誤的做法,斷言,你不能達到預期目的。事實(shí)上,我們可以在Connect()調用之后等待CAsyncSocket::OnConnect()事件被觸發(fā),CAsyncSocket::OnConnect()是要表明Socket要么連接成功了,要么連接徹底失敗了。至此,我們在CAsyncSocket::OnConnect()被調用之后就知道是否Socket連接成功了,還是失敗了。
類(lèi)似的,Send()如果返回WSAEWOULDBLOCK錯誤,我們在OnSend()處等待,Receive()如果返回WSAEWOULDBLOCK錯誤,我們在OnReceive()處等待,以此類(lèi)推。
還有一點(diǎn),也許是個(gè)難點(diǎn),那就是在客戶(hù)方調用Connect()連接服務(wù)方,那么服務(wù)方如何Accept(),以建立連接的問(wèn)題。簡(jiǎn)單的做法就是在監聽(tīng)的Socket收到OnAccept()時(shí),用一個(gè)新的CAsyncSocket對象去建立連接,例如:
BOOL CSocket::Connect( ... )
{
if( !CAsyncSocket::Connect( ... ) )
{
if( WSAGetLastError() == WSAEWOULDBLOCK ) //由于異步操作需要時(shí)間,不能立即完成,所以Socket返回這個(gè)錯誤
{
//進(jìn)入消息循環(huán),以從線(xiàn)程消息隊列里查看FD_CONNECT消息,直到收到FD_CONNECT消息,認為連接成功。
while( PumpMessages( FD_CONNECT ) );
}
}
}
BOOL CSocket::PumpMessages( UINT uEvent )
{
CWinThread* pThread = AfxGetThread();
while( bBlocking ) //bBlocking僅僅是一個(gè)標志,看用戶(hù)是否取消對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(); //處理消息隊列里的其它消息
pThread->OnIdle(-1);
}
}
}
BOOL CSocket::OnMessagePending()
{
MSG msg;
if( PeekMessage( &msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE ) )
{ //這里僅關(guān)心WM_PAINT消息,以處理阻塞期間的主窗口重畫(huà)
::DispatchMessage( &msg );
return FALSE;
}
return FALSE;
}
CSocketFile file( pSocket );
CArchive ar( &file, CArchive::store );
pDocument->Serialize( ar );
ar.Close();
同樣,接收一方可以只改變上面的代碼為CArchive ar( &file, CArchive::load );即可。
注意到,CSocketFile類(lèi)雖然從CFile派生,但它屏蔽掉了CFile::Open()等函數,而函數里僅扔出一個(gè)例外。那么也就是說(shuō),你不能調用CSocketFile的Open函數來(lái)打開(kāi)一個(gè)實(shí)實(shí)在在的文件,否則會(huì )導致例外,如果你需要利用CSocketFile來(lái)傳送文件,你必須提供CSocketFile類(lèi)的這些函數的實(shí)現。
再一點(diǎn),CArchive不支持在datagram的Socket連接上序列化數據
本文來(lái)自CSDN博客,轉載請標明出處:http://blog.csdn.net/phlexii/archive/2006/06/27/839053.aspx
聯(lián)系客服