版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、 微軟的MFC把復(fù)雜的WinSock API函數(shù)封裝到類里,這使得編寫網(wǎng)絡(luò)應(yīng)用程序更容易。CAsyncSocket類逐個(gè)封裝了WinSock API,為高級(jí)網(wǎng)絡(luò)程序員提供了更加有力而靈活的方法。這個(gè)類基于程序員了解網(wǎng)絡(luò)通訊的假設(shè),目的是為了在MFC中使用WinSock,程序員有責(zé)任處理諸如阻塞、字節(jié)順序和在Unicode與MBCS 間轉(zhuǎn)換字符的任務(wù)。為了給程序員提供更方便的接口以自動(dòng)處理這些任務(wù),MFC給出了CSocket類,這個(gè)類是由CAsyncSocket類繼承下來的,它提供了比CAsyncSocket更高層的WinSock API接口。CSocket類和CSocketFile類
2、可以與CArchive類一起合作來管理發(fā)送和接收的數(shù)據(jù),這使管理數(shù)據(jù)收發(fā)更加便利。CSocket對(duì)象提供阻塞模式,這對(duì)于CArchive的同步操作是至關(guān)重要的。阻塞函數(shù)(如Receive()、Send()、ReceiveFrom()、SendTo() 和Accept())直到操作完成后才返回控制權(quán),因此如果需要低層控制和高效率,就使用CAsyncSock類;如果需要方便,則可使用CSocket類。 CSocket類是由CAsyncSocket繼承而來的,事實(shí)上,在MFC中CAsyncSocket 逐個(gè)封裝了WinSock API,每個(gè)CAsyncSocket對(duì)象代表一個(gè)Windows
3、 Socket對(duì)象,使用CAsyncSocket 類要求程序員對(duì)網(wǎng)絡(luò)編程較為熟悉。相比起來,CSocket類是CAsyncSocket的派生類,繼承了它封裝的WinSock API。一個(gè)CSocket對(duì)象代表了一個(gè)比CAsyncSocket對(duì)象更高層次的Windows Socket的抽象,CSocket類與CSocketFile類和CArchive類一起工作來發(fā)送和接收數(shù)據(jù),因此使用它更加容易使用。CSocket對(duì)象提供阻塞模式,因?yàn)樽枞δ軐?duì)于CArchive的同步操作是至關(guān)重要的。在這里有必要對(duì)阻塞的概念作一解釋:一個(gè)socket可以處于"阻塞模式"或"非阻塞
4、模式",當(dāng)一個(gè)套接字處于阻塞模式(即同步操作)時(shí),它的阻塞函數(shù)直到操作完成才會(huì)返回控制權(quán),之所以稱為阻塞是因?yàn)榇颂捉幼值淖枞瘮?shù)在完成操作返回之前什么也不能做。如果一個(gè)socket處于非阻塞模式(即異步操作),則會(huì)被調(diào)用函數(shù)立即返回。在CAsyncSocket類中可以用GetLastError 成員函數(shù)查詢最后的錯(cuò)誤,如果錯(cuò)誤是WSAEWOULDBLOCK則說明有阻塞,而CSocket絕不會(huì)返回WSAEWOULDBLOCK,因?yàn)樗约汗芾碜枞N④浗ㄗh盡量使用非阻塞模式,通過網(wǎng)絡(luò)事件的發(fā)生而通知應(yīng)用程序進(jìn)行相應(yīng)的處理。但在CSocket類中,為了利用CArchive 處理通訊中的許多
5、問題和簡(jiǎn)化編程,它的一些成員函數(shù)總是具有阻塞性質(zhì)的,這是因?yàn)镃Archive類需要同步的操作。在Win32環(huán)境下,如果要使用具有阻塞性質(zhì)的套接字,應(yīng)該放在獨(dú)立的工作線程中處理,利用多線程的方法使阻塞不至于干擾其他線程,也不會(huì)把CPU時(shí)間浪費(fèi)在阻塞上。多線程的方法既可以使程序員享受CSocket帶來的簡(jiǎn)化編程的便利,也不會(huì)影響用戶界面對(duì)用戶的反應(yīng)。 MFC疑難注解:CAsyncSocket及CSocket。CSocket從CAsyncSocket派生,但是其功能已經(jīng)由異步轉(zhuǎn)換成同步。MFC對(duì)SOCKET編程的支持其實(shí)是很充分的,然而其文檔是語焉不詳?shù)?。以至?/p>
6、大多數(shù)用VC編寫的功能稍復(fù)雜的網(wǎng)絡(luò)程序,還是使用API的。故CAsyncSocket及CSocket事實(shí)上成為疑難,群眾多敬而遠(yuǎn)之。余好事者也,不忍資源浪費(fèi),特為之注解。一、CAsyncSocket與CSocket的區(qū)別前者是異步通信,后者是同步通信;前者是非阻塞模式,后者是阻塞模式。另外,異步非阻塞模式有時(shí)也被稱為長(zhǎng)連接,同步阻塞模式則被稱為短連接。為了更明白地講清楚兩者的區(qū)別,舉個(gè)例子:設(shè)想你是一位體育老師,需要測(cè)驗(yàn)100位同學(xué)的400米成績(jī)。你當(dāng)然不會(huì)讓100位同學(xué)一起起跑,因?yàn)楫?dāng)同學(xué)們返回終點(diǎn)時(shí),你根本來不及掐表記錄各位同學(xué)的成績(jī)。如果你每次讓一位同學(xué)起跑并等待他回到終點(diǎn)你記下成績(jī)后再
7、讓下一位起跑,直到所有同學(xué)都跑完。恭喜你,你已經(jīng)掌握了同步阻塞模式。你設(shè)計(jì)了一個(gè)函數(shù),傳入?yún)?shù)是學(xué)生號(hào)和起跑時(shí)間,返回值是到達(dá)終點(diǎn)的時(shí)間。你調(diào)用該函數(shù)100次,就能完成這次測(cè)驗(yàn)任務(wù)。這個(gè)函數(shù)是同步的,因?yàn)橹灰阏{(diào)用它,就能得到結(jié)果;這個(gè)函數(shù)也是阻塞的,因?yàn)槟阋坏┱{(diào)用它,就必須等待,直到它給你結(jié)果,不能去干其他事情。如果你一邊每隔10秒讓一位同學(xué)起跑,直到所有同學(xué)出發(fā)完畢;另一邊每有一個(gè)同學(xué)回到終點(diǎn)就記錄成績(jī),直到所有同學(xué)都跑完。恭喜你,你已經(jīng)掌握了異步非阻塞模式。你設(shè)計(jì)了兩個(gè)函數(shù),其中一個(gè)函數(shù)記錄起跑時(shí)間和學(xué)生號(hào),該函數(shù)你會(huì)主動(dòng)調(diào)用100次;另一個(gè)函數(shù)記錄到達(dá)時(shí)間和學(xué)生號(hào),該函數(shù)是一個(gè)事件驅(qū)動(dòng)
8、的callback函數(shù),當(dāng)有同學(xué)到達(dá)終點(diǎn)時(shí),你會(huì)被動(dòng)調(diào)用。你主動(dòng)調(diào)用的函數(shù)是異步的,因?yàn)槟阏{(diào)用它,它并不會(huì)告訴你結(jié)果;這個(gè)函數(shù)也是非阻塞的,因?yàn)槟阋坏┱{(diào)用它,它就馬上返回,你不用等待就可以再次調(diào)用它。但僅僅將這個(gè)函數(shù)調(diào)用100次,你并沒有完成你的測(cè)驗(yàn)任務(wù),你還需要被動(dòng)等待調(diào)用另一個(gè)函數(shù)100次。當(dāng)然,你馬上就會(huì)意識(shí)到,同步阻塞模式的效率明顯低于異步非阻塞模式。那么,誰還會(huì)使用同步阻塞模式呢?不錯(cuò),異步模式效率高,但更麻煩,你一邊要記錄起跑同學(xué)的數(shù)據(jù),一邊要記錄到達(dá)同學(xué)的數(shù)據(jù),而且同學(xué)們回到終點(diǎn)的次序與起跑的次序并不相同,所以你還要不停地在你的成績(jī)冊(cè)上查找學(xué)生號(hào)。忙亂之中你往往會(huì)張冠李戴。你可能
9、會(huì)想出更聰明的辦法:你帶了很多塊秒表,讓同學(xué)們分組互相測(cè)驗(yàn)。恭喜你!你已經(jīng)掌握了多線程同步模式!每個(gè)拿秒表的同學(xué)都可以獨(dú)立調(diào)用你的同步函數(shù),這樣既不容易出錯(cuò),效率也大大提高,只要秒表足夠多,同步的效率也能達(dá)到甚至超過異步??梢岳斫猓悻F(xiàn)的問題可能是:既然多線程同步既快又好,異步模式還有存在的必要嗎?很遺憾,異步模式依然非常重要,因?yàn)樵诤芏嗲闆r下,你拿不出很多秒表。你需要通信的對(duì)端系統(tǒng)可能只允許你建立一個(gè)SOCKET連接,很多金融、電信行業(yè)的大型業(yè)務(wù)系統(tǒng)都如此要求。現(xiàn)在,你應(yīng)該已經(jīng)明白了:CAsyncSocket用于在少量連接時(shí),處理大批量無步驟依賴性的業(yè)務(wù)。CSocket用于處理步驟依賴性業(yè)務(wù)
10、,或在可多連接時(shí)配合多線程使用。二、CAsyncSocket異步機(jī)制當(dāng)你獲得了一個(gè)異步連接后,實(shí)際上你掃除了發(fā)送動(dòng)作與接收動(dòng)作之間的依賴性。所以你隨時(shí)可以發(fā)包,也隨時(shí)可能收到包。發(fā)送、接收函數(shù)都是異步非阻塞的,頃刻就能返回,所以收發(fā)交錯(cuò)進(jìn)行著,你可以一直工作,保持很高的效率。但是,正因?yàn)榘l(fā)送、接收函數(shù)都是異步非阻塞的,所以僅調(diào)用它們并不能保障發(fā)送或接收的完成。例如發(fā)送函數(shù)Send,調(diào)用它可能有4種結(jié)果:1、錯(cuò)誤,Send()=SOCKET_ERROR,GetLastError()!=WSAEWOULDBLOCK,這種情況可能由各種網(wǎng)絡(luò)問題導(dǎo)致,你需要馬上決定是放棄本次操作,還是啟用某種對(duì)策2、
11、忙,Send()=SOCKET_ERROR,GetLastError()=WSAEWOULDBLOCK,導(dǎo)致這種情況的原因是,你的發(fā)送緩沖區(qū)已被填滿或?qū)Ψ降慕邮芫彌_區(qū)已被填滿。這種情況你實(shí)際上不用馬上理睬。因?yàn)镃AsyncSocket會(huì)記得你的Send WSAEWOULDBLOCK了,待發(fā)送的數(shù)據(jù)會(huì)寫入CAsyncSocket內(nèi)部的發(fā)送緩沖區(qū),并會(huì)在不忙的時(shí)候自動(dòng)調(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不會(huì)記得你只完成了部分發(fā)送任務(wù)從而在合適的時(shí)候觸發(fā)OnSend,因?yàn)槟悴]有WSAEWOULDBLOCK。你可能認(rèn)為既然已經(jīng)填滿緩沖區(qū),繼續(xù)發(fā)送必然會(huì)WSAEWOULDBLOCK,其實(shí)不然,假如WSAEWOULDBLOCK是由于對(duì)方讀取接收緩沖區(qū)不及時(shí)引起的,繼續(xù)發(fā)送的確很可能會(huì)WSAEWOULDB
13、LOCK,但假如WSAEWOULDBLOCK是由于發(fā)送緩沖區(qū)被填滿,就不一定了,因?yàn)槟愕木W(wǎng)卡處理發(fā)送緩沖區(qū)中數(shù)據(jù)的速度不見得比你往發(fā)送緩沖區(qū)拷貝數(shù)據(jù)的速度更慢,這要取決與你競(jìng)爭(zhēng)CPU、內(nèi)存、帶寬資源的其他應(yīng)用程序的具體情況。假如這時(shí)候CPU負(fù)載較大而網(wǎng)卡負(fù)載較低,則雖然剛剛發(fā)送緩沖區(qū)是滿的,你繼續(xù)發(fā)送也不會(huì)WSAEWOULDBLOCK。4、完成,Send(pBuf,nLen)=nLen與OnSend協(xié)助Send完成工作一樣,OnRecieve、OnConnect、OnAccept也會(huì)分別協(xié)助Recieve、Connect、Accept完成工作。這一切都通過消息機(jī)制完成:在你使用CAsyncSo
14、cket之前,必須調(diào)用AfxSocketInit初始化WinSock環(huán)境,而AfxSocketInit會(huì)創(chuàng)建一個(gè)隱藏的CSocketWnd對(duì)象,由于這個(gè)對(duì)象由Cwnd派生,因此它能夠接收Windows消息。所以它能夠成為高層CAsyncSocket對(duì)象與WinSock底層之間的橋梁。例如某CAsyncSocket在Send時(shí)WSAEWOULDBLOCK了,它就會(huì)發(fā)送一條消息給CSocketWnd作為報(bào)告,CSocketWnd會(huì)維護(hù)一個(gè)報(bào)告登記表,當(dāng)它收到底層WinSock發(fā)出的空閑消息時(shí),就會(huì)檢索報(bào)告登記表,然后直接調(diào)用報(bào)告者的OnSend函數(shù)。所以前文所說的CAsyncSocket會(huì)自動(dòng)調(diào)用
15、OnXxx,實(shí)際上是不對(duì)的,真正的調(diào)用者是CSocketWnd它是一個(gè)CWnd對(duì)象,運(yùn)行在獨(dú)立的線程中。使用CAsyncSocket時(shí),Send流程和Recieve流程是不同的,不理解這一點(diǎn)就不可能順利使用CAsyncSocket。MSDN對(duì)CAsyncSocket的解釋很容易讓你理解為:只有OnSend被觸發(fā)時(shí)你Send才有意義,你才應(yīng)該Send,同樣只有OnRecieve被觸發(fā)時(shí)你才應(yīng)該Recieve。很不幸,你錯(cuò)了:你會(huì)發(fā)現(xiàn),連接建立的同時(shí),OnSend就第一次被觸發(fā)了,嗯,這很好,但你現(xiàn)在還不想Send,你讓OnSend返回,干點(diǎn)其他的事情,等待下一次OnSend試試看?實(shí)際上,你再也
16、等不到OnSend被觸發(fā)了。因?yàn)?,除了第一次以外,OnSend的任何一次觸發(fā),都源于你調(diào)用了Send,但碰到了WSAEWOULDBLOCK!所以,使用CAsyncSocket時(shí),針對(duì)發(fā)送的流程邏輯應(yīng)該是:你需兩個(gè)成員變量,一個(gè)發(fā)送任務(wù)表,一個(gè)記錄發(fā)送進(jìn)度。你可以,也應(yīng)該,在任何你需要的時(shí)候,主動(dòng)調(diào)用Send來發(fā)送數(shù)據(jù),同時(shí)更新任務(wù)表和發(fā)送進(jìn)度。而OnSend,則是你的負(fù)責(zé)擦屁股工作的助手,它被觸發(fā)時(shí)要干的事情就是根據(jù)任務(wù)表和發(fā)送進(jìn)度調(diào)用Send繼續(xù)發(fā)。若又沒能將任務(wù)表全部發(fā)送完成,更新發(fā)送進(jìn)度,退出,等待下一次OnSend;若任務(wù)表已全部發(fā)送完畢,則清空任務(wù)表及發(fā)送進(jìn)度。使用CAsyncSoc
17、ket的接收流程邏輯是不同的:你永遠(yuǎn)不需要主動(dòng)調(diào)用Recieve,你只應(yīng)該在OnRecieve中等待。由于你不可能知道將要抵達(dá)的數(shù)據(jù)類型及次序,所以你需要定義一個(gè)已收數(shù)據(jù)表作為成員變量來存儲(chǔ)已收到但尚未處理的數(shù)據(jù)。每次OnRecieve被觸發(fā),你只需要被動(dòng)調(diào)用一次Recieve來接受固定長(zhǎng)度的數(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中被再次組合、掃描并處理。在長(zhǎng)連接應(yīng)用中,連接可能因?yàn)楦鞣N原因中斷,所
18、以你需要自動(dòng)重連。你需要根據(jù)CAsyncSocket的成員變量m_hSocket來判斷當(dāng)前連接狀態(tài):if(m_hSocket=INVALID_SOCKET)。當(dāng)然,很奇怪的是,即使連接已經(jīng)中斷,OnClose也已經(jīng)被觸發(fā),你還是需要在OnClose中主動(dòng)調(diào)用Close,否則m_hSocket并不會(huì)被自動(dòng)賦值為INVALID_SOCKET。在很多長(zhǎng)連接應(yīng)用中,除建立連接以外,還需要先Login,然后才能進(jìn)行業(yè)務(wù)處理,連接并Login是一個(gè)步驟依賴性過程,用異步方式處理反而會(huì)很麻煩,而CAsyncSocket是支持切換為同步模式的,你應(yīng)該掌握在適當(dāng)?shù)臅r(shí)候切換同異步模式的方法:DWORD dw;/切
19、換為同步模式dw=0;IOCtl(FIONBIO,&dw);./切換回異步模式dw=1;IOCtl(FIONBIO,&dw);三、CSocket的用法CSocket在CAsyncSocket的基礎(chǔ)上,修改了Send、Recieve等成員函數(shù),幫你內(nèi)置了一個(gè)用以輪詢收發(fā)緩沖區(qū)的循環(huán),變成了同步短連接模式。短連接應(yīng)用簡(jiǎn)單明了,CSocket經(jīng)常不用派生就可以直接使用,但也有些問題:1、用作監(jiān)聽的時(shí)候曾經(jīng)看到有人自己創(chuàng)建線程,在線程中創(chuàng)建CSocket對(duì)象進(jìn)行Listen、Accept,若Accept成功則再起一個(gè)線程繼續(xù)Listen、Accept??梢哉f他完全不理解CSocket,
20、實(shí)際上CSocket的監(jiān)聽機(jī)制已經(jīng)內(nèi)置了多線程機(jī)制,你只需要從CSocket派生,然后重載OnAccept:/CListenSocket頭文件class CListenSocket : public CSocketpublic: CListenSocket(HWND hWnd=NULL); HWND m_hWnd; /事件處理窗口 virtual void OnAccept(int nErrorCode);/CListenSocket實(shí)現(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、用于多線程的時(shí)候??吹饺苏fCSocket在子線程中不能用,其實(shí)不然。實(shí)際情況是:直接使用CSocket動(dòng)態(tài)創(chuàng)建的對(duì)象,將其指針作為參數(shù)傳遞給子線程,則子線程中進(jìn)行收發(fā)等各種操作都沒問題。但如果是使用CSocket派生類創(chuàng)建的對(duì)象,就要看你重載了哪些方法,假如你僅重載了OnClose,則子線程中你也可以正常收發(fā),但不能Close!因?yàn)镃Socket是用內(nèi)部循環(huán)做到同步的,并不依賴各OnXxx,它不需要與CSocketWnd交互。但當(dāng)你派生并重載OnXxx后,它為了提供消息機(jī)制
26、就必須與CSocketWnd交互。當(dāng)你調(diào)用AfxSocketInit時(shí),你的主線程會(huì)獲得一個(gè)訪問CSocketWnd的句柄,對(duì)CSocketWnd的訪問是MFC自動(dòng)幫你完成的,是被隱藏的。而你自己創(chuàng)建的子線程并不自動(dòng)具備訪問CSocketWnd的機(jī)制,所以子線程中需要訪問CSocketWnd的操作都會(huì)失敗。??吹降慕鉀Q辦法是給子線程傳遞SOCKET句柄而不是CSocket對(duì)象指針,然后在子線程中創(chuàng)建CSocket臨時(shí)對(duì)象并Attach傳入的句柄,用完后再Dettach并delete臨時(shí)對(duì)象。俺沒有這么干過,估計(jì)是因?yàn)锳ttach方法含有獲取CSocketWnd句柄的內(nèi)置功能。俺的解決方案還是使
27、用自定義消息,比如俺不能在子線程中Close,那么,俺可以給主線程發(fā)送一條消息,讓主線程的消息處理函數(shù)來完成Close,也很方便。CSocket一般配合多線程使用,只要你想收發(fā)數(shù)據(jù),你就可以創(chuàng)建一個(gè)CSocket對(duì)象,并創(chuàng)建一個(gè)子線程來進(jìn)行收發(fā)。所以被阻塞的只是子線程,而主線程總是可以隨時(shí)創(chuàng)建子線程去幫它干活。由于可能同時(shí)有很多個(gè)CSocket對(duì)象在工作,所以你一般還要?jiǎng)?chuàng)建一個(gè)列表來儲(chǔ)存這些CSocket對(duì)象的標(biāo)識(shí),這樣你可能通過在列表中檢索標(biāo)識(shí)來區(qū)分各個(gè)CSocket對(duì)象,當(dāng)然,由于內(nèi)存地址的唯一性,對(duì)象指針本身就可以作為標(biāo)識(shí)。相對(duì)CAsyncSocket而言,CSocket的運(yùn)作流程更直觀
28、也更簡(jiǎn)單。四、技術(shù)內(nèi)幕 Socket有同步阻塞方式和異步非阻塞方式兩種使用,事實(shí)上同步和異步在我們編程的生涯中可能遇到了很多,而Socket也沒什么特別。雖然同步好用,不費(fèi)勁,但不能滿足一些應(yīng)用場(chǎng)合,其效率也很低。 也許初涉編程的人不能理解“同步(或阻塞)”和“異步(或非阻塞)”,其實(shí)簡(jiǎn)單兩句話就能講清楚,同步和異步往往都是針對(duì)一個(gè)函數(shù)來說的,“同步”就是函數(shù)直到其要執(zhí)行的功能全部完成時(shí)才返回,而“異步”則是,函數(shù)僅僅做一些簡(jiǎn)單的工作,然后馬上返回,而它所要實(shí)現(xiàn)的功能留給別的線程或者函數(shù)去完
29、成。例如,SendMessage就是“同步”函數(shù),它不但發(fā)送消息到消息隊(duì)列,還需要等待消息被執(zhí)行完才返回;相反PostMessage就是個(gè)異步函數(shù),它只管發(fā)送一個(gè)消息,而不管這個(gè)消息是否被處理,就馬上返回。<一>、Socket API 首先應(yīng)該知道,有Socket1.1提供的原始API函數(shù),和Socket2.0提供的一組擴(kuò)展函數(shù),兩套函數(shù)。這兩套函數(shù)有重復(fù),但是2.0提供的函數(shù)功能更強(qiáng)大,函數(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ù)被用作阻塞方式,所以可能你需要放在專門的線程里,這樣以不影響主程序的運(yùn)行和主窗口的刷新。2、如果作為異步用,那么程序主要就是要處理事件。它有兩種處理事件的辦法:
31、0; 第一種,它常關(guān)聯(lián)一個(gè)窗口,也就是異步Socket的事件將作為消息發(fā)往該窗口,這是由WinSock擴(kuò)展規(guī)范里的一個(gè)函數(shù)WSAAsyncSelect()來實(shí)現(xiàn)和窗口關(guān)聯(lián)。最終你只需要處理窗口消息,來收發(fā)數(shù)據(jù)。 第二種,用到了擴(kuò)展規(guī)范里另一個(gè)關(guān)于事件的函數(shù)WSAEventSelect(),它是用事件對(duì)象的方式來處理Socket事件,也就是,你必須首先用WSACreateEvent()來創(chuàng)建一個(gè)事件對(duì)象,然后調(diào)用WSAEventSelect()來使得Socket的事件和這個(gè)事件對(duì)象關(guān)聯(lián)。最終你將要在一個(gè)線程里用WSAWaitForMulti
32、pleEvents()來等待這個(gè)事件對(duì)象被觸發(fā)。這個(gè)過程也稍顯復(fù)雜。<二>、CAsyncSocket 看類名就知道,它是一個(gè)異步非阻塞Socket封裝類,CAsyncSocket:Create()有一個(gè)參數(shù)指明了你想要處理哪些Socket事件,你關(guān)心的事件被指定以后,這個(gè)Socket默認(rèn)就被用作了異步方式。CAsyncSocket是在UI線程中使用的,不需要多線程。那么CAsyncSocket內(nèi)部到底是如何將事件交給你的呢? CAsyncSocket的Create()函數(shù),除了創(chuàng)建了一個(gè)SO
33、CKET以外,還創(chuàng)建了個(gè)CSocketWnd窗口對(duì)象,并使用WSAAsyncSelect()將這個(gè)SOCKET與該窗口對(duì)象關(guān)聯(lián),以讓該窗口對(duì)象處理來自Socket的事件(消息),然而CSocketWnd收到Socket事件之后,只是簡(jiǎn)單地回調(diào)CAsyncSocket:OnReceive(),CAsyncSocket:OnSend(),CAsyncSocket:OnAccept(),CAsyncSocket:OnConnect()等虛函數(shù)。所以CAsyncSocket的派生類,只需要在這些虛函數(shù)里添加發(fā)送和接收的代碼。 簡(jiǎn)化后,大致的代碼為:
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)事件的窗口,實(shí)際的這個(gè)窗口在AfxSockInit()調(diào)用時(shí)就被創(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ā)這個(gè)事件的Socket的句柄 int nErrorCode = WSAGETSELECTERR
37、OR(lParam); file:/lParam/是錯(cuò)誤碼與事件碼的合成 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編程的人理解的,也是本文最要提醒的一點(diǎn)是,客戶方在使用CAsyncSocket:Connect()時(shí),往往返回一個(gè)WSAEWOULDBLOCK的錯(cuò)誤(其它的某些函數(shù)調(diào)用也如此),實(shí)際上這不應(yīng)該算作一個(gè)錯(cuò)誤,它是Socket提醒我們,由于你使
42、用了非阻塞Socket方式,所以(連接)操作需要時(shí)間,不能瞬間建立。既然如此,我們可以等待呀,等它連接成功為止,于是許多程序員就在調(diào)用Connect()之后,Sleep(0),然后不停地用WSAGetLastError()或者CAsyncSocket:GetLastError()查看Socket返回的錯(cuò)誤,直到返回成功為止。這是一種錯(cuò)誤的做法,斷言,你不能達(dá)到預(yù)期目的。事實(shí)上,我們可以在Connect()調(diào)用之后等待CAsyncSocket:OnConnect()事件被觸發(fā),CAsyncSocket:OnConnect()是要表明Socket要么連接成功了,要么連接徹底失敗了。至此,我們?cè)贑A
43、syncSocket:OnConnect()被調(diào)用之后就知道是否Socket連接成功了,還是失敗了。 類似的,Send()如果返回WSAEWOULDBLOCK錯(cuò)誤,我們?cè)贠nSend()處等待,Receive()如果返回WSAEWOULDBLOCK錯(cuò)誤,我們?cè)贠nReceive()處等待,以此類推。 還有一點(diǎn),也許是個(gè)難點(diǎn),那就是在客戶方調(diào)用Connect()連接服務(wù)方,那么服務(wù)方如何Accept(),以建立連接的問題。簡(jiǎn)單的做法就是在監(jiān)聽的Socket收到OnAccept()時(shí),用一個(gè)新的CAsyncSocket對(duì)象去建立連接,例如: v
44、oid CMySocket:OnAccept( int ErrCode ) CMySocket* pSocket = new CMySocket; Accept( *pSocket ); 于是,上面的pSocket和客戶方建立了連接,以后的通信就是這個(gè)pSocket
45、對(duì)象去和客戶方進(jìn)行,而監(jiān)聽的Socket仍然繼續(xù)在監(jiān)聽,一旦又有一個(gè)客戶方要連接服務(wù)方,則上面的OnAccept()又會(huì)被調(diào)用一次。當(dāng)然pSocket是和客戶方通信的服務(wù)方,它不會(huì)觸發(fā)OnAccept()事件,因?yàn)樗皇潜O(jiān)聽Socket。<三>、CSocket CSocket是MFC在CAsyncSocket基礎(chǔ)上派生的一個(gè)同步阻塞Socket的封裝類。它是如何又把CAsyncSocket變成同步的,而且還能響應(yīng)同樣的Socket事件呢? 其實(shí)很簡(jiǎn)單,CSocket在Connect()返回WSAEWOULDBLOCK錯(cuò)誤時(shí),不
46、是在OnConnect(),OnReceive()這些事件終端函數(shù)里去等待。你先必須明白Socket事件是如何到達(dá)這些事件函數(shù)里的。這些事件處理函數(shù)是靠CSocketWnd窗口對(duì)象回調(diào)的,而窗口對(duì)象收到來自Socket的事件,又是靠線程消息隊(duì)列分發(fā)過來的。總之,Socket事件首先是作為一個(gè)消息發(fā)給CSocketWnd窗口對(duì)象,這個(gè)消息肯定需要經(jīng)過線程消息隊(duì)列的分發(fā),最終CSocketWnd窗口對(duì)象收到這些消息就調(diào)用相應(yīng)的回調(diào)函數(shù)(OnConnect()等)。 所以,CSocket在調(diào)用Connect()之后,如果返回一個(gè)WSAEWOULDBLOCK錯(cuò)誤時(shí),它
47、馬上進(jìn)入一個(gè)消息循環(huán),就是從當(dāng)前線程的消息隊(duì)列里取關(guān)心的消息,如果取到了WM_PAINT消息,則刷新窗口,如果取到的是Socket發(fā)來的消息,則根據(jù)Socket是否有操作錯(cuò)誤碼,調(diào)用相應(yīng)的回調(diào)函數(shù)(OnConnect()等)。 大致的簡(jiǎn)化代碼為: BOOL CSocket:Connect( . ) if( !CAsyncSocket:Connect( . ) )
48、160; if( WSAGetLastError() = WSAEWOULDBLOCK ) file:/由/于異步操作需要時(shí)間,不能立即完成,所以Socket返回這個(gè)錯(cuò)誤 file:/進(jìn)/入消息循環(huán),以從線程消息隊(duì)列里查看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/僅僅是一個(gè)標(biāo)志,看用戶是否取消對(duì)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:/處/理消息隊(duì)列里的其它消息
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 五年級(jí)數(shù)學(xué)口算100題
- 昆明冶金高等??茖W(xué)?!夺t(yī)學(xué)文獻(xiàn)檢索1》2023-2024學(xué)年第一學(xué)期期末試卷
- 江蘇食品藥品職業(yè)技術(shù)學(xué)院《中外文學(xué)名著欣賞藏》2023-2024學(xué)年第一學(xué)期期末試卷
- 吉林建筑大學(xué)《商務(wù)統(tǒng)計(jì)實(shí)訓(xùn)》2023-2024學(xué)年第一學(xué)期期末試卷
- 湖南軟件職業(yè)技術(shù)大學(xué)《GIS軟件應(yīng)用實(shí)驗(yàn)(一)》2023-2024學(xué)年第一學(xué)期期末試卷
- 湖北幼兒師范高等專科學(xué)?!哆^程原理》2023-2024學(xué)年第一學(xué)期期末試卷
- 【物理】《跨學(xué)科實(shí)踐:制作微型密度計(jì)》(教學(xué)設(shè)計(jì))-2024-2025學(xué)年人教版(2024)初中物理八年級(jí)下冊(cè)
- 高考物理總復(fù)習(xí)《功和功率、動(dòng)能定理》專項(xiàng)測(cè)試卷含答案
- 中國(guó)民航大學(xué)《中級(jí)財(cái)務(wù)會(huì)計(jì)Ⅱ》2023-2024學(xué)年第一學(xué)期期末試卷
- 鄭州理工職業(yè)學(xué)院《服裝展示設(shè)計(jì)》2023-2024學(xué)年第一學(xué)期期末試卷
- 2025年湖北武漢工程大學(xué)招聘6人歷年高頻重點(diǎn)提升(共500題)附帶答案詳解
- 【數(shù) 學(xué)】2024-2025學(xué)年北師大版數(shù)學(xué)七年級(jí)上冊(cè)期末能力提升卷
- 山東省建筑工程消防設(shè)計(jì)部分非強(qiáng)制性條文適用指引
- 內(nèi)蒙古自治區(qū)呼和浩特市《綜合能力測(cè)試》事業(yè)單位國(guó)考真題
- 陜西省咸陽市各縣區(qū)鄉(xiāng)鎮(zhèn)行政村村莊村名居民村民委員會(huì)明細(xì)及行政區(qū)劃代碼
- 綠城物業(yè)室內(nèi)公共區(qū)域清潔作業(yè)規(guī)程
- 封條模板A4直接打印版
- 危險(xiǎn)貨物道路運(yùn)輸企業(yè)安全檢查通用清單
- 用友NC財(cái)務(wù)軟件操作手冊(cè)
- 眼內(nèi)炎患者護(hù)理查房
- 電工維修培訓(xùn)資料 維修電工技術(shù)學(xué)習(xí) 維修電工常識(shí) 電工培訓(xùn)ppt課件
評(píng)論
0/150
提交評(píng)論