大致的代碼為
bool CAsyncSocket::Create( long lEvent ) //參數lEvent是指定你所關心的Socket事件
{
m_hSocket = socket( PF_INET,
SOCK_STREAM, 0 ); //創建Socket對象
CSocketWnd* pSockWnd = new CSocketWnd; //創建回響事件的視窗,實際的這個視窗在AfxSockInit()調用時就被創建了。
pSockWnd->Create(...);
WSAAsyncSelect( m_hSocket, pSockWnd->m_hWnd, WM_SOCKET_NOTIFY, lEvent ); //Socket 事件和視窗關聯
}
static void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket*pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE); //注意這裡獲取的是已經與m_socketwnd關聯的
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類大致為:
END_MESSAGE_MAP()
LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket::DoCallBack( wParam, lParam ); //收到Socket事件訊息,
回調CAsyncSocket的DoCallBack()函式
return 0L;
}
然而,最不容易被初學Socket編程的人理解的,也是本文最要提醒的一點是,客戶方在使用CAsyncSocket::Connect()時,往往返回一個WSAEWOULDBLOCK的錯誤(其它的某些
函式調用也如此),實際上這不應該算作一個錯誤,它是Socket提醒我們,由於你使用了非阻塞Socket方式,所以(連線)操作需要時間,不能瞬間建立。既然如此,我們可以等待呀,等它連線成功為止,於是許多程式設計師就在調用Connect()之後,Sleep(0),然後不停地用WSAGetLastError()或者CAsyncSocket::
GetLastError()查看Socket返回的錯誤,直到返回成功為止。這是一種錯誤的做法,斷言,你不能達到預期目的。事實上,我們可以在Connect()調用之後等待CAsyncSocket::OnConnect()事件被觸發,CAsyncSocket::OnConnect()是要表明Socket要么連線成功了,要么連線徹底失敗了。至此,我們在CAsyncSocket::OnConnect()被調用之後就知道是否Socket連線成功了,還是失敗了。
類似的,Send()如果返回WSAEWOULDBLOCK錯誤,我們在OnSend()處等待,Receive()如果返回WSAEWOULDBLOCK錯誤,我們在OnReceive()處等待,以此類推。
特別注意
還有一點,也許是個難點,那就是在客戶方調用Connect()連線服務方,那么服務方如何Accept(),以建立連線的問題。簡單的做法就是在監聽的Socket收到OnAccept()時,用一個新的CAsyncSocket對象去建立連線,例如:
void CMySocket::OnAccept( int ErrCode )
{
CMySocket* pSocket = new CMySocket;
Accept( *pSocket );
}
於是,上面的pSocket和客戶方建立了連線,以後的通信就是這個pSocket對象去和客戶方進行,而監聽的Socket仍然繼續在監聽,一旦又有一個客戶方要連線服務方,則上面的OnAccept()又會被調用一次。當然pSocket是和客戶方通信的服務方,它不會觸發OnAccept()事件,因為它不是監聽Socket。
CAsyncsocket擴展
Tim kosse 改進了MFC的CAsyncsocket,封裝為CAsyncsocketEx。