CAsyncSocket

它是一個異步非阻塞Socket封裝類,CAsyncSocket::Create()有一個參數指明了你想要處理哪些Socket事件,你關心的事件被指定以後,這個Socket默認就被用作了異步方式。CAsyncSocket的Create()函式,除了創建了一個SOCKET以外,還創建了個CSocketWnd視窗對象,並使用WSAAsyncSelect()將這個SOCKET與該視窗對象關聯,以讓該視窗對象處理來自Socket的事件(訊息),然而CSocketWnd收到Socket事件之後,只是簡單地回調CAsyncSocket::OnReceive()等虛函式。所以CAsyncSocket的派生類,只需要在這些虛函數裡添加傳送和接收的代碼。

大致的代碼為

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類大致為:

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;

}

然而,最不容易被初學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。

相關詞條

相關搜尋

熱門詞條

聯絡我們