版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、 微軟的MFC把復(fù)雜的WinSock API函數(shù)封裝到類里,這使得編寫網(wǎng)絡(luò)應(yīng)用程序更容易。CAsyncSocket類逐個封裝了WinSock API,為高級網(wǎng)絡(luò)程序員提供了更加有力而靈活的方法。這個類基于程序員了解網(wǎng)絡(luò)通訊的假設(shè),目的是為了在MFC中使用WinSock,程序員有責(zé)任處理諸如阻塞、字節(jié)順序和在Unicode與MBCS 間轉(zhuǎn)換字符的任務(wù)。為了給程序員提供更方便的接口以自動處理這些任務(wù),MFC給出了CSocket類,這個類是由CAsyncSocket類繼承下來的,它提供了比CAsyncSocket更高層的WinSock API接口。CSocket類和CSocketFile類
2、可以與CArchive類一起合作來管理發(fā)送和接收的數(shù)據(jù),這使管理數(shù)據(jù)收發(fā)更加便利。CSocket對象提供阻塞模式,這對于CArchive的同步操作是至關(guān)重要的。阻塞函數(shù)(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制權(quán),因此如果需要低層控制和高效率,就使用CAsyncSock類;如果需要方便,則可使用CSocket類。 CSocket類是由CAsyncSocket繼承而來的,事實上,在MFC中CAsyncSocket 逐個封裝了WinSock API,每個CAsyncSocket對象代表一個Windows
3、 Socket對象,使用CAsyncSocket 類要求程序員對網(wǎng)絡(luò)編程較為熟悉。相比起來,CSocket類是CAsyncSocket的派生類,繼承了它封裝的WinSock API。一個CSocket對象代表了一個比CAsyncSocket對象更高層次的Windows Socket的抽象,CSocket類與CSocketFile類和CArchive類一起工作來發(fā)送和接收數(shù)據(jù),因此使用它更加容易使用。CSocket對象提供阻塞模式,因為阻塞功能對于CArchive的同步操作是至關(guān)重要的。在這里有必要對阻塞的概念作一解釋:一個socket可以處于"阻塞模式"或"非阻塞
4、模式",當(dāng)一個套接字處于阻塞模式(即同步操作)時,它的阻塞函數(shù)直到操作完成才會返回控制權(quán),之所以稱為阻塞是因為此套接字的阻塞函數(shù)在完成操作返回之前什么也不能做。如果一個socket處于非阻塞模式(即異步操作),則會被調(diào)用函數(shù)立即返回。在CAsyncSocket類中可以用GetLastError 成員函數(shù)查詢最后的錯誤,如果錯誤是WSAEWOULDBLOCK則說明有阻塞,而CSocket絕不會返回WSAEWOULDBLOCK,因為它自己管理阻塞。微軟建議盡量使用非阻塞模式,通過網(wǎng)絡(luò)事件的發(fā)生而通知應(yīng)用程序進行相應(yīng)的處理。但在CSocket類中,為了利用CArchive 處理通訊中的許多
5、問題和簡化編程,它的一些成員函數(shù)總是具有阻塞性質(zhì)的,這是因為CArchive類需要同步的操作。在Win32環(huán)境下,如果要使用具有阻塞性質(zhì)的套接字,應(yīng)該放在獨立的工作線程中處理,利用多線程的方法使阻塞不至于干擾其他線程,也不會把CPU時間浪費在阻塞上。多線程的方法既可以使程序員享受CSocket帶來的簡化編程的便利,也不會影響用戶界面對用戶的反應(yīng)。 MFC疑難注解:CAsyncSocket及CSocket。CSocket從CAsyncSocket派生,但是其功能已經(jīng)由異步轉(zhuǎn)換成同步。MFC對SOCKET編程的支持其實是很充分的,然而其文檔是語焉不詳?shù)?。以至?/p>
6、大多數(shù)用VC編寫的功能稍復(fù)雜的網(wǎng)絡(luò)程序,還是使用API的。故CAsyncSocket及CSocket事實上成為疑難,群眾多敬而遠之。余好事者也,不忍資源浪費,特為之注解。一、CAsyncSocket與CSocket的區(qū)別前者是異步通信,后者是同步通信;前者是非阻塞模式,后者是阻塞模式。另外,異步非阻塞模式有時也被稱為長連接,同步阻塞模式則被稱為短連接。為了更明白地講清楚兩者的區(qū)別,舉個例子:設(shè)想你是一位體育老師,需要測驗100位同學(xué)的400米成績。你當(dāng)然不會讓100位同學(xué)一起起跑,因為當(dāng)同學(xué)們返回終點時,你根本來不及掐表記錄各位同學(xué)的成績。如果你每次讓一位同學(xué)起跑并等待他回到終點你記下成績后再
7、讓下一位起跑,直到所有同學(xué)都跑完。恭喜你,你已經(jīng)掌握了同步阻塞模式。你設(shè)計了一個函數(shù),傳入?yún)?shù)是學(xué)生號和起跑時間,返回值是到達終點的時間。你調(diào)用該函數(shù)100次,就能完成這次測驗任務(wù)。這個函數(shù)是同步的,因為只要你調(diào)用它,就能得到結(jié)果;這個函數(shù)也是阻塞的,因為你一旦調(diào)用它,就必須等待,直到它給你結(jié)果,不能去干其他事情。如果你一邊每隔10秒讓一位同學(xué)起跑,直到所有同學(xué)出發(fā)完畢;另一邊每有一個同學(xué)回到終點就記錄成績,直到所有同學(xué)都跑完。恭喜你,你已經(jīng)掌握了異步非阻塞模式。你設(shè)計了兩個函數(shù),其中一個函數(shù)記錄起跑時間和學(xué)生號,該函數(shù)你會主動調(diào)用100次;另一個函數(shù)記錄到達時間和學(xué)生號,該函數(shù)是一個事件驅(qū)動
8、的callback函數(shù),當(dāng)有同學(xué)到達終點時,你會被動調(diào)用。你主動調(diào)用的函數(shù)是異步的,因為你調(diào)用它,它并不會告訴你結(jié)果;這個函數(shù)也是非阻塞的,因為你一旦調(diào)用它,它就馬上返回,你不用等待就可以再次調(diào)用它。但僅僅將這個函數(shù)調(diào)用100次,你并沒有完成你的測驗任務(wù),你還需要被動等待調(diào)用另一個函數(shù)100次。當(dāng)然,你馬上就會意識到,同步阻塞模式的效率明顯低于異步非阻塞模式。那么,誰還會使用同步阻塞模式呢?不錯,異步模式效率高,但更麻煩,你一邊要記錄起跑同學(xué)的數(shù)據(jù),一邊要記錄到達同學(xué)的數(shù)據(jù),而且同學(xué)們回到終點的次序與起跑的次序并不相同,所以你還要不停地在你的成績冊上查找學(xué)生號。忙亂之中你往往會張冠李戴。你可能
9、會想出更聰明的辦法:你帶了很多塊秒表,讓同學(xué)們分組互相測驗。恭喜你!你已經(jīng)掌握了多線程同步模式!每個拿秒表的同學(xué)都可以獨立調(diào)用你的同步函數(shù),這樣既不容易出錯,效率也大大提高,只要秒表足夠多,同步的效率也能達到甚至超過異步??梢岳斫?,你現(xiàn)的問題可能是:既然多線程同步既快又好,異步模式還有存在的必要嗎?很遺憾,異步模式依然非常重要,因為在很多情況下,你拿不出很多秒表。你需要通信的對端系統(tǒng)可能只允許你建立一個SOCKET連接,很多金融、電信行業(yè)的大型業(yè)務(wù)系統(tǒng)都如此要求。現(xiàn)在,你應(yīng)該已經(jīng)明白了:CAsyncSocket用于在少量連接時,處理大批量無步驟依賴性的業(yè)務(wù)。CSocket用于處理步驟依賴性業(yè)務(wù)
10、,或在可多連接時配合多線程使用。二、CAsyncSocket異步機制當(dāng)你獲得了一個異步連接后,實際上你掃除了發(fā)送動作與接收動作之間的依賴性。所以你隨時可以發(fā)包,也隨時可能收到包。發(fā)送、接收函數(shù)都是異步非阻塞的,頃刻就能返回,所以收發(fā)交錯進行著,你可以一直工作,保持很高的效率。但是,正因為發(fā)送、接收函數(shù)都是異步非阻塞的,所以僅調(diào)用它們并不能保障發(fā)送或接收的完成。例如發(fā)送函數(shù)Send,調(diào)用它可能有4種結(jié)果:1、錯誤,Send()=SOCKET_ERROR,GetLastError()!=WSAEWOULDBLOCK,這種情況可能由各種網(wǎng)絡(luò)問題導(dǎo)致,你需要馬上決定是放棄本次操作,還是啟用某種對策2、
11、忙,Send()=SOCKET_ERROR,GetLastError()=WSAEWOULDBLOCK,導(dǎo)致這種情況的原因是,你的發(fā)送緩沖區(qū)已被填滿或?qū)Ψ降慕邮芫彌_區(qū)已被填滿。這種情況你實際上不用馬上理睬。因為CAsyncSocket會記得你的Send WSAEWOULDBLOCK了,待發(fā)送的數(shù)據(jù)會寫入CAsyncSocket內(nèi)部的發(fā)送緩沖區(qū),并會在不忙的時候自動調(diào)用OnSend,發(fā)送內(nèi)部緩沖區(qū)里的數(shù)據(jù)。3、部分完成,0<Send(pBuf,nLen)<nLen,導(dǎo)致這種情況的原因是,你的發(fā)送緩沖區(qū)或?qū)Ψ降慕邮站彌_區(qū)中剩余的空位不足以容納你這次需要發(fā)送的全部數(shù)據(jù)。處理這種情況的通常
12、做法是繼續(xù)發(fā)送尚未發(fā)送的數(shù)據(jù)直到全部完成或WSAEWOULDBLOCK。這種情況很容易讓人產(chǎn)生疑惑,既然緩沖區(qū)空位不足,那么本次發(fā)送就已經(jīng)填滿了緩沖區(qū),干嘛還要繼續(xù)發(fā)送呢,就像WSAEWOULDBLOCK了一樣直接交給OnSend去處理剩余數(shù)據(jù)的發(fā)送不是更合理嗎?然而很遺憾,CAsyncSocket不會記得你只完成了部分發(fā)送任務(wù)從而在合適的時候觸發(fā)OnSend,因為你并沒有WSAEWOULDBLOCK。你可能認(rèn)為既然已經(jīng)填滿緩沖區(qū),繼續(xù)發(fā)送必然會WSAEWOULDBLOCK,其實不然,假如WSAEWOULDBLOCK是由于對方讀取接收緩沖區(qū)不及時引起的,繼續(xù)發(fā)送的確很可能會WSAEWOULDB
13、LOCK,但假如WSAEWOULDBLOCK是由于發(fā)送緩沖區(qū)被填滿,就不一定了,因為你的網(wǎng)卡處理發(fā)送緩沖區(qū)中數(shù)據(jù)的速度不見得比你往發(fā)送緩沖區(qū)拷貝數(shù)據(jù)的速度更慢,這要取決與你競爭CPU、內(nèi)存、帶寬資源的其他應(yīng)用程序的具體情況。假如這時候CPU負載較大而網(wǎng)卡負載較低,則雖然剛剛發(fā)送緩沖區(qū)是滿的,你繼續(xù)發(fā)送也不會WSAEWOULDBLOCK。4、完成,Send(pBuf,nLen)=nLen與OnSend協(xié)助Send完成工作一樣,OnRecieve、OnConnect、OnAccept也會分別協(xié)助Recieve、Connect、Accept完成工作。這一切都通過消息機制完成:在你使用CAsyncSo
14、cket之前,必須調(diào)用AfxSocketInit初始化WinSock環(huán)境,而AfxSocketInit會創(chuàng)建一個隱藏的CSocketWnd對象,由于這個對象由Cwnd派生,因此它能夠接收Windows消息。所以它能夠成為高層CAsyncSocket對象與WinSock底層之間的橋梁。例如某CAsyncSocket在Send時WSAEWOULDBLOCK了,它就會發(fā)送一條消息給CSocketWnd作為報告,CSocketWnd會維護一個報告登記表,當(dāng)它收到底層WinSock發(fā)出的空閑消息時,就會檢索報告登記表,然后直接調(diào)用報告者的OnSend函數(shù)。所以前文所說的CAsyncSocket會自動調(diào)用
15、OnXxx,實際上是不對的,真正的調(diào)用者是CSocketWnd它是一個CWnd對象,運行在獨立的線程中。使用CAsyncSocket時,Send流程和Recieve流程是不同的,不理解這一點就不可能順利使用CAsyncSocket。MSDN對CAsyncSocket的解釋很容易讓你理解為:只有OnSend被觸發(fā)時你Send才有意義,你才應(yīng)該Send,同樣只有OnRecieve被觸發(fā)時你才應(yīng)該Recieve。很不幸,你錯了:你會發(fā)現(xiàn),連接建立的同時,OnSend就第一次被觸發(fā)了,嗯,這很好,但你現(xiàn)在還不想Send,你讓OnSend返回,干點其他的事情,等待下一次OnSend試試看?實際上,你再也
16、等不到OnSend被觸發(fā)了。因為,除了第一次以外,OnSend的任何一次觸發(fā),都源于你調(diào)用了Send,但碰到了WSAEWOULDBLOCK!所以,使用CAsyncSocket時,針對發(fā)送的流程邏輯應(yīng)該是:你需兩個成員變量,一個發(fā)送任務(wù)表,一個記錄發(fā)送進度。你可以,也應(yīng)該,在任何你需要的時候,主動調(diào)用Send來發(fā)送數(shù)據(jù),同時更新任務(wù)表和發(fā)送進度。而OnSend,則是你的負責(zé)擦屁股工作的助手,它被觸發(fā)時要干的事情就是根據(jù)任務(wù)表和發(fā)送進度調(diào)用Send繼續(xù)發(fā)。若又沒能將任務(wù)表全部發(fā)送完成,更新發(fā)送進度,退出,等待下一次OnSend;若任務(wù)表已全部發(fā)送完畢,則清空任務(wù)表及發(fā)送進度。使用CAsyncSoc
17、ket的接收流程邏輯是不同的:你永遠不需要主動調(diào)用Recieve,你只應(yīng)該在OnRecieve中等待。由于你不可能知道將要抵達的數(shù)據(jù)類型及次序,所以你需要定義一個已收數(shù)據(jù)表作為成員變量來存儲已收到但尚未處理的數(shù)據(jù)。每次OnRecieve被觸發(fā),你只需要被動調(diào)用一次Recieve來接受固定長度的數(shù)據(jù),并添加到你的已收數(shù)據(jù)表后。然后你需要掃描已收數(shù)據(jù)表,若其中已包含一條或數(shù)條完整的可解析的業(yè)務(wù)數(shù)據(jù)包,截取出來,調(diào)用業(yè)務(wù)處理窗口的處理函數(shù)來處理或作為消息參數(shù)發(fā)送給業(yè)務(wù)處理窗口。而已收數(shù)據(jù)表中剩下的數(shù)據(jù),將等待下次OnRecieve中被再次組合、掃描并處理。在長連接應(yīng)用中,連接可能因為各種原因中斷,所
18、以你需要自動重連。你需要根據(jù)CAsyncSocket的成員變量m_hSocket來判斷當(dāng)前連接狀態(tài):if(m_hSocket=INVALID_SOCKET)。當(dāng)然,很奇怪的是,即使連接已經(jīng)中斷,OnClose也已經(jīng)被觸發(fā),你還是需要在OnClose中主動調(diào)用Close,否則m_hSocket并不會被自動賦值為INVALID_SOCKET。在很多長連接應(yīng)用中,除建立連接以外,還需要先Login,然后才能進行業(yè)務(wù)處理,連接并Login是一個步驟依賴性過程,用異步方式處理反而會很麻煩,而CAsyncSocket是支持切換為同步模式的,你應(yīng)該掌握在適當(dāng)?shù)臅r候切換同異步模式的方法:DWORD dw;/切
19、換為同步模式dw=0;IOCtl(FIONBIO,&dw);./切換回異步模式dw=1;IOCtl(FIONBIO,&dw);三、CSocket的用法CSocket在CAsyncSocket的基礎(chǔ)上,修改了Send、Recieve等成員函數(shù),幫你內(nèi)置了一個用以輪詢收發(fā)緩沖區(qū)的循環(huán),變成了同步短連接模式。短連接應(yīng)用簡單明了,CSocket經(jīng)常不用派生就可以直接使用,但也有些問題:1、用作監(jiān)聽的時候曾經(jīng)看到有人自己創(chuàng)建線程,在線程中創(chuàng)建CSocket對象進行Listen、Accept,若Accept成功則再起一個線程繼續(xù)Listen、Accept??梢哉f他完全不理解CSocket,
20、實際上CSocket的監(jiān)聽機制已經(jīng)內(nèi)置了多線程機制,你只需要從CSocket派生,然后重載OnAccept:/CListenSocket頭文件class CListenSocket : public CSocketpublic: CListenSocket(HWND hWnd=NULL); HWND m_hWnd; /事件處理窗口 virtual void OnAccept(int nErrorCode);/CListenSocket實現(xiàn)文件#include "ListenSo
21、cket.h"CListenSocket:CListenSocket(HWND hWnd)m_hWnd=hWnd;void CListenSocket:OnAccept(int nErrorCode) SendMessage(m_hWnd,WM_SOCKET_MSG,SOCKET_CLNT_ACCEPT,0); CSocket:OnAccept(nErrorCode);/主線程.m_pListenSocket=new CListenSocket(m_hWnd);m_pListenSocket->Create
22、(.);m_pListenSocket->Listen();.LRESULT CXxxDlg:OnSocketMsg(WPARAM wParam, LPARAM lParam) UINT type=(UINT)wParam; switch(type) case SOCKET_CLNT_ACCEPT: &
23、#160; CSocket* pSocket=new CSocket; if(!m_pListenSocket->Accept(*pSocket) &
24、#160; delete pSocket; break; .
25、; . 2、用于多線程的時候常看到人說CSocket在子線程中不能用,其實不然。實際情況是:直接使用CSocket動態(tài)創(chuàng)建的對象,將其指針作為參數(shù)傳遞給子線程,則子線程中進行收發(fā)等各種操作都沒問題。但如果是使用CSocket派生類創(chuàng)建的對象,就要看你重載了哪些方法,假如你僅重載了OnClose,則子線程中你也可以正常收發(fā),但不能Close!因為CSocket是用內(nèi)部循環(huán)做到同步的,并不依賴各OnXxx,它不需要與CSocketWnd交互。但當(dāng)你派生并重載OnXxx后,它為了提供消息機制
26、就必須與CSocketWnd交互。當(dāng)你調(diào)用AfxSocketInit時,你的主線程會獲得一個訪問CSocketWnd的句柄,對CSocketWnd的訪問是MFC自動幫你完成的,是被隱藏的。而你自己創(chuàng)建的子線程并不自動具備訪問CSocketWnd的機制,所以子線程中需要訪問CSocketWnd的操作都會失敗。??吹降慕鉀Q辦法是給子線程傳遞SOCKET句柄而不是CSocket對象指針,然后在子線程中創(chuàng)建CSocket臨時對象并Attach傳入的句柄,用完后再Dettach并delete臨時對象。俺沒有這么干過,估計是因為Attach方法含有獲取CSocketWnd句柄的內(nèi)置功能。俺的解決方案還是使
27、用自定義消息,比如俺不能在子線程中Close,那么,俺可以給主線程發(fā)送一條消息,讓主線程的消息處理函數(shù)來完成Close,也很方便。CSocket一般配合多線程使用,只要你想收發(fā)數(shù)據(jù),你就可以創(chuàng)建一個CSocket對象,并創(chuàng)建一個子線程來進行收發(fā)。所以被阻塞的只是子線程,而主線程總是可以隨時創(chuàng)建子線程去幫它干活。由于可能同時有很多個CSocket對象在工作,所以你一般還要創(chuàng)建一個列表來儲存這些CSocket對象的標(biāo)識,這樣你可能通過在列表中檢索標(biāo)識來區(qū)分各個CSocket對象,當(dāng)然,由于內(nèi)存地址的唯一性,對象指針本身就可以作為標(biāo)識。相對CAsyncSocket而言,CSocket的運作流程更直觀
28、也更簡單。四、技術(shù)內(nèi)幕 Socket有同步阻塞方式和異步非阻塞方式兩種使用,事實上同步和異步在我們編程的生涯中可能遇到了很多,而Socket也沒什么特別。雖然同步好用,不費勁,但不能滿足一些應(yīng)用場合,其效率也很低。 也許初涉編程的人不能理解“同步(或阻塞)”和“異步(或非阻塞)”,其實簡單兩句話就能講清楚,同步和異步往往都是針對一個函數(shù)來說的,“同步”就是函數(shù)直到其要執(zhí)行的功能全部完成時才返回,而“異步”則是,函數(shù)僅僅做一些簡單的工作,然后馬上返回,而它所要實現(xiàn)的功能留給別的線程或者函數(shù)去完
29、成。例如,SendMessage就是“同步”函數(shù),它不但發(fā)送消息到消息隊列,還需要等待消息被執(zhí)行完才返回;相反PostMessage就是個異步函數(shù),它只管發(fā)送一個消息,而不管這個消息是否被處理,就馬上返回。<一>、Socket API 首先應(yīng)該知道,有Socket1.1提供的原始API函數(shù),和Socket2.0提供的一組擴展函數(shù),兩套函數(shù)。這兩套函數(shù)有重復(fù),但是2.0提供的函數(shù)功能更強大,函數(shù)數(shù)量也更多。這兩套函數(shù)可以靈活混用,分別包含在頭文件Winsock.h,Winsock2.h,分別需要引入庫wsock32.lib、Ws
30、2_32.lib。1、默認(rèn)用作同步阻塞方式,那就是當(dāng)你從不調(diào)用WSAIoctl()和ioctlsocket()來改變Socket IO模式,也從不調(diào)用WSAAsyncSelect()和WSAEventSelect()來選擇需要處理的Socket事件。正是由于函數(shù)accept(),WSAAccept(),connect(),WSAConnect(),send(),WSASend(),recv(),WSARecv()等函數(shù)被用作阻塞方式,所以可能你需要放在專門的線程里,這樣以不影響主程序的運行和主窗口的刷新。2、如果作為異步用,那么程序主要就是要處理事件。它有兩種處理事件的辦法:
31、0; 第一種,它常關(guān)聯(lián)一個窗口,也就是異步Socket的事件將作為消息發(fā)往該窗口,這是由WinSock擴展規(guī)范里的一個函數(shù)WSAAsyncSelect()來實現(xiàn)和窗口關(guān)聯(lián)。最終你只需要處理窗口消息,來收發(fā)數(shù)據(jù)。 第二種,用到了擴展規(guī)范里另一個關(guān)于事件的函數(shù)WSAEventSelect(),它是用事件對象的方式來處理Socket事件,也就是,你必須首先用WSACreateEvent()來創(chuàng)建一個事件對象,然后調(diào)用WSAEventSelect()來使得Socket的事件和這個事件對象關(guān)聯(lián)。最終你將要在一個線程里用WSAWaitForMulti
32、pleEvents()來等待這個事件對象被觸發(fā)。這個過程也稍顯復(fù)雜。<二>、CAsyncSocket 看類名就知道,它是一個異步非阻塞Socket封裝類,CAsyncSocket:Create()有一個參數(shù)指明了你想要處理哪些Socket事件,你關(guān)心的事件被指定以后,這個Socket默認(rèn)就被用作了異步方式。CAsyncSocket是在UI線程中使用的,不需要多線程。那么CAsyncSocket內(nèi)部到底是如何將事件交給你的呢? CAsyncSocket的Create()函數(shù),除了創(chuàng)建了一個SO
33、CKET以外,還創(chuàng)建了個CSocketWnd窗口對象,并使用WSAAsyncSelect()將這個SOCKET與該窗口對象關(guān)聯(lián),以讓該窗口對象處理來自Socket的事件(消息),然而CSocketWnd收到Socket事件之后,只是簡單地回調(diào)CAsyncSocket:OnReceive(),CAsyncSocket:OnSend(),CAsyncSocket:OnAccept(),CAsyncSocket:OnConnect()等虛函數(shù)。所以CAsyncSocket的派生類,只需要在這些虛函數(shù)里添加發(fā)送和接收的代碼。 簡化后,大致的代碼為:
34、0; bool CAsyncSocket:Create( long lEvent ) file:/參數(shù)lEvent是指定你所關(guān)心的Socket事件 m_hSocket = socket( PF_INET, SOCK_STREAM, 0 ); file:/創(chuàng)/建Socket本身 CSocketWnd* pSockWnd = new CSo
35、cketWnd; file:/創(chuàng)建響應(yīng)事件的窗口,實際的這個窗口在AfxSockInit()調(diào)用時就被創(chuàng)建了。 pSockWnd->Create(.); WSAAsyncSelect( m_hSocket, pSockWnd->m_hWnd, WM_SOCKET_NOTIFY, lEvent ); file:/Socket/事件和窗口關(guān)聯(lián) static void
36、60;PASCAL CAsyncSocket:DoCallBack(WPARAM wParam, LPARAM lParam) CAsyncSocket Socket; Socket.Attach( (SOCKET)wParam ); file:/wParam/就是觸發(fā)這個事件的Socket的句柄 int nErrorCode = WSAGETSELECTERR
37、OR(lParam); file:/lParam/是錯誤碼與事件碼的合成 switch (WSAGETSELECTEVENT(lParam) case FD_READ: pSocket->OnReceive(nErrorCode); break; case FD_WRITE:
38、pSocket->OnSend(nErrorCode); break; case FD_OOB: pSocket->OnOutOfBandData(nErrorCode); break; case FD_ACCEPT: pSocket->OnAccept(nErrorCode);
39、60; break; case FD_CONNECT: pSocket->OnConnect(nErrorCode); break; case FD_CLOSE: pSocket->OnClose(nErrorCode); break;
40、; CSocketWnd類大致為: BEGIN_MESSAGE_MAP(CSocketWnd, CWnd) ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify) END_MESSAGE_MAP() LRESULT CSocketWnd:OnSocketNotify(WPARAM wParam, LPARAM lParam) CAsyn
41、cSocket:DoCallBack( wParam, lParam ); file:/收/到Socket事件消息,回調(diào)CAsyncSocket的DoCallBack()函數(shù) return 0L; 然而,最不容易被初學(xué)Socket編程的人理解的,也是本文最要提醒的一點是,客戶方在使用CAsyncSocket:Connect()時,往往返回一個WSAEWOULDBLOCK的錯誤(其它的某些函數(shù)調(diào)用也如此),實際上這不應(yīng)該算作一個錯誤,它是Socket提醒我們,由于你使
42、用了非阻塞Socket方式,所以(連接)操作需要時間,不能瞬間建立。既然如此,我們可以等待呀,等它連接成功為止,于是許多程序員就在調(diào)用Connect()之后,Sleep(0),然后不停地用WSAGetLastError()或者CAsyncSocket:GetLastError()查看Socket返回的錯誤,直到返回成功為止。這是一種錯誤的做法,斷言,你不能達到預(yù)期目的。事實上,我們可以在Connect()調(diào)用之后等待CAsyncSocket:OnConnect()事件被觸發(fā),CAsyncSocket:OnConnect()是要表明Socket要么連接成功了,要么連接徹底失敗了。至此,我們在CA
43、syncSocket:OnConnect()被調(diào)用之后就知道是否Socket連接成功了,還是失敗了。 類似的,Send()如果返回WSAEWOULDBLOCK錯誤,我們在OnSend()處等待,Receive()如果返回WSAEWOULDBLOCK錯誤,我們在OnReceive()處等待,以此類推。 還有一點,也許是個難點,那就是在客戶方調(diào)用Connect()連接服務(wù)方,那么服務(wù)方如何Accept(),以建立連接的問題。簡單的做法就是在監(jiān)聽的Socket收到OnAccept()時,用一個新的CAsyncSocket對象去建立連接,例如: v
44、oid CMySocket:OnAccept( int ErrCode ) CMySocket* pSocket = new CMySocket; Accept( *pSocket ); 于是,上面的pSocket和客戶方建立了連接,以后的通信就是這個pSocket
45、對象去和客戶方進行,而監(jiān)聽的Socket仍然繼續(xù)在監(jiān)聽,一旦又有一個客戶方要連接服務(wù)方,則上面的OnAccept()又會被調(diào)用一次。當(dāng)然pSocket是和客戶方通信的服務(wù)方,它不會觸發(fā)OnAccept()事件,因為它不是監(jiān)聽Socket。<三>、CSocket CSocket是MFC在CAsyncSocket基礎(chǔ)上派生的一個同步阻塞Socket的封裝類。它是如何又把CAsyncSocket變成同步的,而且還能響應(yīng)同樣的Socket事件呢? 其實很簡單,CSocket在Connect()返回WSAEWOULDBLOCK錯誤時,不
46、是在OnConnect(),OnReceive()這些事件終端函數(shù)里去等待。你先必須明白Socket事件是如何到達這些事件函數(shù)里的。這些事件處理函數(shù)是靠CSocketWnd窗口對象回調(diào)的,而窗口對象收到來自Socket的事件,又是靠線程消息隊列分發(fā)過來的??傊?,Socket事件首先是作為一個消息發(fā)給CSocketWnd窗口對象,這個消息肯定需要經(jīng)過線程消息隊列的分發(fā),最終CSocketWnd窗口對象收到這些消息就調(diào)用相應(yīng)的回調(diào)函數(shù)(OnConnect()等)。 所以,CSocket在調(diào)用Connect()之后,如果返回一個WSAEWOULDBLOCK錯誤時,它
47、馬上進入一個消息循環(huán),就是從當(dāng)前線程的消息隊列里取關(guān)心的消息,如果取到了WM_PAINT消息,則刷新窗口,如果取到的是Socket發(fā)來的消息,則根據(jù)Socket是否有操作錯誤碼,調(diào)用相應(yīng)的回調(diào)函數(shù)(OnConnect()等)。 大致的簡化代碼為: BOOL CSocket:Connect( . ) if( !CAsyncSocket:Connect( . ) )
48、160; if( WSAGetLastError() = WSAEWOULDBLOCK ) file:/由/于異步操作需要時間,不能立即完成,所以Socket返回這個錯誤 file:/進/入消息循環(huán),以從線程消息隊列里查看FD_CONNECT消息,直到收到FD_CONNECT消息,認(rèn)為連接成功。 while( PumpMessages( FD_CONNE
49、CT ) ); BOOL CSocket:PumpMessages( UINT uEvent ) CWinThread* pThread = AfxGetThread(); while( bBlocking
50、 ) file:/bBlocking/僅僅是一個標(biāo)志,看用戶是否取消對Connect()的調(diào)用 MSG msg; if( PeekMessage( &msg, WM_SOCKET_NOTIFY ) )
51、160; if( msg.message = WM_SOCKET_NOTIFY && WSAGETSELECTEVENT(msg.lParam) = uStopFlag )
52、; CAsyncSocket:DoCallBack( msg.wParam, msg.lParam );
53、 return TRUE; else OnMessagePending(); file:/處/理消息隊列里的其它消息
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024至2030年中國快速切斷電動球閥行業(yè)投資前景及策略咨詢研究報告
- 2024至2030年中國工藝扣數(shù)據(jù)監(jiān)測研究報告
- 2024至2030年中國四大金剛雕塑行業(yè)投資前景及策略咨詢研究報告
- 2024年中國黃油面包市場調(diào)查研究報告
- 2024年中國端面式公制杠桿千分表市場調(diào)查研究報告
- 2024八年級數(shù)學(xué)上冊第一章因式分解3公式法第2課時用完全平方公式分解因式習(xí)題課件魯教版五四制
- 2024年烏魯木齊客運從業(yè)資格證的考試題目是什么題
- 2024年海南客運資格證摸擬考試題
- 2024年杭州客運從業(yè)資格證應(yīng)用能力考試
- 2024年武漢駕駛員客運從業(yè)資格證考試題庫
- 閥門帶壓堵漏技術(shù)(李彪)
- 鈣離子增敏劑對心衰治療帶來的治療革命
- 建筑工程初步設(shè)計文件審查要點
- 《律師參與公司自行清算業(yè)務(wù)操作指引》
- 引水工程施工設(shè)計方案
- 四氫呋喃項目可行性研究報告-用于立項備案
- 部編版《道德與法治》五年級下冊第8課《推翻帝制 民族覺醒》優(yōu)質(zhì)課件
- Q∕GDW 11514-2021 變電站智能機器人巡檢系統(tǒng)檢測規(guī)范
- 基坑支護工程(技術(shù)標(biāo)圖文)
- 汽車美容裝飾行業(yè)員工提成方案
- 布纜船操作規(guī)程
評論
0/150
提交評論