網(wǎng)絡(luò)編程入門

網(wǎng)絡(luò)編程入門

ID:25892368

大小:131.00 KB

頁數(shù):17頁

時間:2018-11-23

上傳者:xinshengwencai
網(wǎng)絡(luò)編程入門_第1頁
網(wǎng)絡(luò)編程入門_第2頁
網(wǎng)絡(luò)編程入門_第3頁
網(wǎng)絡(luò)編程入門_第4頁
網(wǎng)絡(luò)編程入門_第5頁
資源描述:

《網(wǎng)絡(luò)編程入門》由會員上傳分享,免費在線閱讀,更多相關(guān)內(nèi)容在教育資源-天天文庫。

快樂魔導師Ares.vip.myrice.comWindows網(wǎng)絡(luò)編程快樂魔導師Ares.vip.myrice.comQQ:35830152E-Mail:euho@sina.com17 快樂魔導師Ares.vip.myrice.com第一章序言我寫這個專題的目的,一方面是為了通過對網(wǎng)絡(luò)編程再一次系統(tǒng)的總結(jié),提高自己的網(wǎng)絡(luò)編程水平,特別是Windows下的網(wǎng)絡(luò)編程水平。同時,我也希望,能為眾多初學網(wǎng)絡(luò)編程的人提供一點幫助,因為我開始學習網(wǎng)絡(luò)編程的時候,能找到的資料就很少。當然,花錢可以買到翻譯版本的書:)首先向大家推薦一本很好的參考書,NetworkProgrammingforMicrosoftWindows2nd,初學網(wǎng)絡(luò)編程的時候我還不知道有這樣一本好書,只是上各大論壇把能找到的網(wǎng)絡(luò)編程方面的文章和代碼下載下來,然后自己研究。后來看到別人推薦這一本書,下載了一個,看了感覺非常好,里面的內(nèi)容寫得很規(guī)范,條理也很清楚,英文好的朋友可以直接閱讀,不然就只好去弄一本翻譯好的來研究了。、我試著從Windows編程的基礎(chǔ)開始,一直到探索建立高性能的網(wǎng)絡(luò)應用程序。我說過,我并不是以高手的身份寫這本書,而是以和大家一起學習的心態(tài)學習網(wǎng)絡(luò)編程,寫書只是讓自己的思路更清晰,以后還可以翻閱。所以,我不保證書中所有的內(nèi)容都是絕對正確和標準的,有不妥的地方,還希望高手批評指正。這本書是完全免費的,讀者可以任意使用書中的代碼。但是如果需要轉(zhuǎn)載,請注明原作者和出處。如果有商業(yè)運作的需求,請直接和我聯(lián)系。第二章Windows網(wǎng)絡(luò)編程基礎(chǔ)這本書主要探索Windows網(wǎng)絡(luò)編程,開發(fā)平臺是Windows2000和VisualC++.NET,從一個合格的C++程序員到網(wǎng)絡(luò)編程高手,還是需要花不少功夫,至少我認為寫一個聊天程序很簡單,而要寫一個能同時響應成千上萬用戶的高性能網(wǎng)絡(luò)程序,的確不容易。這篇文章所介紹的方法也并不是能直接應用于每一個具體的應用程序,只能作為學習的參考資料。開發(fā)高性能網(wǎng)絡(luò)游戲恐怕是促使很多程序員研究網(wǎng)絡(luò)編程的原因(包括我),現(xiàn)在的大型網(wǎng)絡(luò)游戲?qū)ν瑫r在線人數(shù)的要求比較高,真正的項目往往采取多個服務器(組)負荷分擔的方式工作,我將首先把注意力放到單個服務器的情況。大家都知道,我們用得最多的協(xié)議是UDP和TCP,UDP是不可靠傳輸服務,TCP是可靠傳輸服務。UDP就像點對點的數(shù)據(jù)傳輸一樣,發(fā)送者把數(shù)據(jù)打包,包上有收信者的地址和其他必要信息,至于收信者能不能收到,UDP協(xié)議并不保證。而TCP協(xié)議就像(實際他們是一個層次的網(wǎng)絡(luò)協(xié)議)是建立在UDP的基礎(chǔ)上,加入了校驗和重傳等復雜的機制來保證數(shù)據(jù)可靠的傳達到收信者。關(guān)于網(wǎng)絡(luò)協(xié)議的具體內(nèi)容,讀者可以參考專門介紹網(wǎng)絡(luò)協(xié)議的書籍,或者查看RFC中的有關(guān)內(nèi)容。本書直接探討編程實現(xiàn)網(wǎng)絡(luò)程序的問題。17 快樂魔導師Ares.vip.myrice.com2.1WindowSocket介紹WindowsSocket是從UNIXSocket繼承發(fā)展而來,最新的版本是2.2。進行Windows網(wǎng)絡(luò)編程,你需要在你的程序中包含WINSOCK2.H或MSWSOCK.H,同時你需要添加引入庫WS2_32.LIB或WSOCK32.LIB。準備好后,你就可以著手建立你的第一個網(wǎng)絡(luò)程序了。Socket編程有阻塞和非阻塞兩種,在操作系統(tǒng)I/O實現(xiàn)時又有幾種模型,包括Select,WSAAsyncSelect,WSAEventSelect,IO重疊模型,完成端口等。要學習基本的網(wǎng)絡(luò)編程概念,可以選擇從阻塞模式開始,而要開發(fā)真正實用的程序,就要進行非阻塞模式的編程(很難想象一個大型服務器采用阻塞模式進行網(wǎng)絡(luò)通信)。在選擇I/O模型時,我建議初學者可以從WSAAsyncSelect模型開始,因為它比較簡單,而且有一定的實用性。但是,幾乎所有人都認識到,要開發(fā)同時響應成千上萬用戶的網(wǎng)絡(luò)程序,完成端口模型是最好的選擇。既然完成端口模型是最好的選擇,那為什么我們不直接寫出一個使用完成端口的程序,然后大家稍加修改就OK了。我認為這確實是一個好的想法,但是真正做項目的時候,不同的情況對程序有不同的要求,如果不深入學習網(wǎng)絡(luò)編程的各方面知識,是不可能寫出符合要求的程序,在學習網(wǎng)絡(luò)編程以前,我建議讀者先學習一下網(wǎng)絡(luò)協(xié)議。2.2第一個網(wǎng)絡(luò)程序由于服務器/客戶端模式的網(wǎng)絡(luò)應用比較多,而且服務器端的設(shè)計是重點和難點。所以我想首先探討服務器的設(shè)計方法,在完成服務器的設(shè)計后再探討其他模式的網(wǎng)絡(luò)程序。設(shè)計一個基本的網(wǎng)絡(luò)服務器有以下幾個步驟:1、初始化WindowsSocket2、創(chuàng)建一個監(jiān)聽的Socket3、設(shè)置服務器地址信息,并將監(jiān)聽端口綁定到這個地址上4、開始監(jiān)聽5、接受客戶端連接6、和客戶端通信7、結(jié)束服務并清理WindowsSocket和相關(guān)數(shù)據(jù),或者返回第4步我們可以看出設(shè)計一個最簡單的服務器并不需要太多的代碼,它完全可以做一個小型的聊天程序,或進行數(shù)據(jù)的傳輸。但是這只是我們的開始,我們的最終目的是建立一個有大規(guī)模響應能力的網(wǎng)絡(luò)服務器。如果讀者對操作系統(tǒng)部分的線程使用還有疑問,我建議你現(xiàn)在就開始復習,因為我們經(jīng)常使用線程來提高程序性能,其實線程就是讓CPU不停的工作,而不是總在等待I/O,或者是一個CPI,累死了還是一個CPU。千萬不要以為線程越多的服務器,它的性能就越好,線程的切換也是需要消耗時間的,對于I/O等待少的程序,線程越多性能反而越低。下面是簡單的服務器和客戶端源代碼。(阻塞模式下的,供初學者理解)17 快樂魔導師Ares.vip.myrice.comTCPServer#includevoidmain(void){WSADATAwsaData;SOCKETListeningSocket;SOCKETNewConnection;SOCKADDR_INServerAddr;SOCKADDR_INClientAddr;intPort=5150;//初始化WindowsSocket2.2WSAStartup(MAKEWORD(2,2),&wsaData);//創(chuàng)建一個新的Socket來響應客戶端的連接請求ListeningSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//填寫服務器地址信息//端口為5150//IP地址為INADDR_ANY,注意使用htonl將IP地址轉(zhuǎn)換為網(wǎng)絡(luò)格式ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(Port);ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY);//綁定監(jiān)聽端口bind(ListeningSocket,(SOCKADDR*)&ServerAddr,sizeof(ServerAddr));//開始監(jiān)聽,指定最大同時連接數(shù)為5listen(ListeningSocket,5);//接受新的連接NewConnection=accept(ListeningSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLen));//新的連接建立后,就可以互相通信了,在這個簡單的例子中,我們直接關(guān)閉連接,17 快樂魔導師Ares.vip.myrice.com//并關(guān)閉監(jiān)聽Socket,然后退出應用程序//closesocket(NewConnection);closesocket(ListeningSocket);//釋放WindowsSocketDLL的相關(guān)資源WSACleanup();}TCPClient#includevoidmain(void){WSADATAwsaData;SOCKETs;SOCKADDR_INServerAddr;intPort=5150;//初始化WindowsSocket2.2WSAStartup(MAKEWORD(2,2),&wsaData);//創(chuàng)建一個新的Socket來連接服務器s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//填寫客戶端地址信息//端口為5150//服務器IP地址為"136.149.3.29",注意使用inet_addr將IP地址轉(zhuǎn)換為網(wǎng)絡(luò)格式ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(Port);ServerAddr.sin_addr.s_addr=inet_addr("136.149.3.29");//向服務器發(fā)出連接請求connect(s,(SOCKADDR*)&ServerAddr,sizeof(ServerAddr));//新的連接建立后,就可以互相通信了,在這個簡單的例子中,我們直接關(guān)閉連接,17 快樂魔導師Ares.vip.myrice.com//并關(guān)閉監(jiān)聽Socket,然后退出應用程序closesocket(s);//釋放WindowsSocketDLL的相關(guān)資源WSACleanup();}2.3WSAAsyncSelect模式前面說過,Windows網(wǎng)絡(luò)編程模式有好幾種,他們各有特點,實現(xiàn)起來復雜程度各不相同,適用范圍也不一樣。下圖是NetworkProgrammingforMicrosoftWindows2nd一書中對不同模式的一個性能測試結(jié)果。服務器采用Pentium41.7GHzXeon的CPU,768M內(nèi)存;客戶端有3臺PC,配置分別是Pentium2233MHz,128MB內(nèi)存,Pentium2350MHz,128MB內(nèi)存,Itanium733MHz,1GB內(nèi)存。具體的結(jié)果分析大家可以看看原書中作者的敘述,我關(guān)心的是哪種模式是我需要的。首先是服務器,勿庸置疑,肯定是完成端口模式。那么客戶端呢,當然也可以采用完成端口,但是不同模式是在不同的操作系統(tǒng)下支持的,看下圖:完成端口在Windows98下是不支持的,雖然我們可以假定所有的用戶都已經(jīng)裝上了Windows2000和WindowsXP,。但是,如果是商業(yè)程序,這種想法在現(xiàn)階段不應該有,我們不能讓用戶為了使用我們的客戶端而去升級他的操作系統(tǒng)。Overlapped17 快樂魔導師Ares.vip.myrice.comI/O可以在Windows98下實現(xiàn),性能也不錯,但是實現(xiàn)和理解起來快趕上完成端口了。而且,最關(guān)鍵的一點,客戶端程序不是用來進行大規(guī)模網(wǎng)絡(luò)響應的,客戶端的主要工作應該是進行諸如圖形運算等非網(wǎng)絡(luò)方面的任務。原書作者,包括我強烈推薦大家使用WSAAsyncSelect模式實現(xiàn)客戶端,因為它實現(xiàn)起來比較直接和容易,而且他完全可以滿足客戶端編程的需求。下面是一段源代碼,雖然我們是用它來寫客戶端,我還是把它的服務端代碼放上來,一方面是有興趣的朋友可以用他做測試和了解如何用它實現(xiàn)服務器;另一方面是客戶端的代碼可以很容易的從它修改而成,不同的地方只要參考一下2.1節(jié)里的代碼就知道了。#defineWM_SOCKETWM_USER+1#include#includeintWINAPIWinMain(HINSTANCEhInstance,HINSTANCEhPrevInstance,LPSTRlpCmdLine,intnCmdShow){WSADATAwsd;SOCKETListen;SOCKADDR_INInternetAddr;HWNDWindow;//創(chuàng)建主窗口Window=CreateWindow();//初始化WindowsSocket2.2WSAStartup(MAKEWORD(2,2),&wsd);//創(chuàng)建監(jiān)聽SocketListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//設(shè)置服務器地址InternetAddr.sin_family=AF_INET;InternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);InternetAddr.sin_port=htons(5150);//綁定Socketbind(Listen,(PSOCKADDR)&InternetAddr,sizeof(InternetAddr));//設(shè)置Windows消息,這樣當有Socket事件發(fā)生時,窗口就能收到對應的消息通知//服務器一般設(shè)置FD_ACCEPT│FD_READ|FD_CLOSE//客戶端一般設(shè)置FD_CONNECT│FD_READ|FD_CLOSEWSAAsyncSelect(Listen,Window,WM_SOCKET,FD_ACCEPT│FD_READ|FD_CLOSE);17 快樂魔導師Ares.vip.myrice.com//開始監(jiān)聽listen(Listen,5);//Translateanddispatchwindowmessages//untiltheapplicationterminateswhile(1){//...}}BOOLCALLBACKServerWinProc(HWNDhDlg,UINTwMsg,WPARAMwParam,LPARAMlParam){SOCKETAccept;switch(wMsg){caseWM_PAINT://Processwindowpaintmessagesbreak;caseWM_SOCKET://Determinewhetheranerroroccurredonthe//socketbyusingtheWSAGETSELECTERROR()macroif(WSAGETSELECTERROR(lParam)){//Displaytheerrorandclosethesocketclosesocket((SOCKET)wParam);break;}//Determinewhateventoccurredonthe//socketswitch(WSAGETSELECTEVENT(lParam)){caseFD_ACCEPT://AcceptanincomingconnectionAccept=accept(wParam,NULL,NULL);17 快樂魔導師Ares.vip.myrice.com//Prepareacceptedsocketforread,//write,andclosenotificationWSAAsyncSelect(Accept,hDlg,WM_SOCKET,FD_READ│FD_WRITE│FD_CLOSE);break;caseFD_READ://Receivedatafromthesocketin//wParambreak;caseFD_WRITE://ThesocketinwParamisready//forsendingdatabreak;caseFD_CLOSE://Theconnectionisnowclosedclosesocket((SOCKET)wParam);break;}break;}returnTRUE;}2.4小節(jié)目前為止,我非常簡要的介紹了Windows網(wǎng)絡(luò)編程的一些東西,附上了一些源代碼??梢哉f,讀者特別是初學者,看了后不一定就能馬上寫出程序來,而那些代碼也不是可以直接應用于實際的項目。別急,萬里長征才開始第一步呢,很多書里都是按照基礎(chǔ)到應用的順序來寫的,但是我喜歡更直接一點,更實用一些的方式。而且,我寫的這個專題,畢竟不是商業(yè)化的,時間上不能投入過多,只是作為給初學者的一個小小幫助。更多的還是希望讀者自己刻苦研究,有問題的時候可以到我的論壇上給我留言,以后有機會我也會公布一些實際的代碼。希望結(jié)交更多熱愛編程和中國游戲事業(yè)的朋友。下一章里我將主要講解完成端口編程,這也是我寫這篇文章的初衷,希望對大家能有所幫助。17 快樂魔導師Ares.vip.myrice.com第三章完成端口模式下的高性能網(wǎng)絡(luò)服務器3.1開始完成端口聽起來好像很神秘和復雜,其實并沒有想象的那么難。這方面的文章在論壇上能找到的我差不多都看過,寫得好點的就是CSDN.NET上看到的一組系列文章,不過我認為它只是簡單的翻譯了一下NetworkProgrammingforMicrosoftWindows2nd中的相關(guān)內(nèi)容,附上的代碼好像不是原書中的,可能是另一本外文書里的。我看了以后,覺得還不如看原版的更容易理解。所以在我的開始部分,我主要帶領(lǐng)初學者理解一下完成端口的有關(guān)內(nèi)容,是我開發(fā)的經(jīng)驗,其他的請參考原書的相關(guān)內(nèi)容。采用完成端口的好處是,操作系統(tǒng)的內(nèi)部重疊機制可以保證大量的網(wǎng)絡(luò)請求都被服務器處理,而不是像WSAAsyncSelect和WSAEventSelect的那樣對并發(fā)的網(wǎng)絡(luò)請求有限制,這一點從上一章的測試表格中可以清楚的看出。完成端口就像一種消息通知的機制,我們創(chuàng)建一個線程來不斷讀取完成端口狀態(tài),接收到相應的完成通知后,就進行相應的處理。其實感覺就像WSAAsyncSelect一樣,不過還是有一些的不同。比如我們想接收消息,WSAAsyncSelect會在消息到來的時候直接通知Windows消息循環(huán),然后就可以調(diào)用WSARecv來接收消息了;而完成端口則首先調(diào)用一個WSARecv表示程序需要接收消息(這時可能還沒有任何消息到來),但是只有當消息來的時候WSARecv才算完成,用戶就可以處理消息了,然后再調(diào)用一個WSARecv表示等待下一個消息,如此不停循環(huán),我想這就是完成端口的最大特點吧。Per-handleData和Per-I/OOperationData是兩個比較重要的概念,Per-handleData用來把客戶端數(shù)據(jù)和對應的完成通知關(guān)聯(lián)起來,這樣每次我們處理完成通知的時候,就能知道它是哪個客戶端的消息,并且可以根據(jù)客戶端的信息作出相應的反應,我想也可以理解為Per-ClienthandleData吧。Per-I/OOperationData則不同,它記錄了每次I/O通知的信息,比如接收消息時我們就可以從中讀出消息的內(nèi)容,也就是和I/O操作有關(guān)的信息都記錄在里面了。當你親手實現(xiàn)完成端口的時候就可以理解他們的不同和用途了。CreateIoCompletionPort函數(shù)中有個參數(shù)NumberOfConcurrentThreads,完成端口編程里有個概念WorkerThreads。這里比較容易引起混亂,NumberOfConcurrentThreads需要設(shè)置多少,又需要創(chuàng)建多少個WorkerThreads才算合適?NumberOfConcurrentThreads的數(shù)目和CPU數(shù)量一樣最好,因為少了就沒法利用多CPU的優(yōu)勢,而多了則會因為線程切換造成性能下降。WorkerThreads的數(shù)量是不是也要一樣多呢,當然不是,它的數(shù)量取決于應用程序的需要。舉例來說,我們在WorkerThreads里進行消息處理,如果這個過程中有可能會造成線程阻塞,那如果我們只有一個WorkerThread,我們就不能很快響應其他客戶端的請求了,而只有當這個阻塞操作完成了后才能繼續(xù)處理下一個完成消息。但是如果我們還有其他的WorkerThread,我們就能繼續(xù)處理其他客戶端的請求,所以到底需要多少的WorkerThread,需要根據(jù)應用程序來定,而不是可以事先估算出來的。如果工作者線程里沒有阻塞操作,對于某些情況來說,一個工作者線程就可以滿足需要了。其他問題,NetworkProgrammingforMicrosoftWindows2nd中,作者還提出了如何安全的退出應用程序等等實現(xiàn)中的細節(jié)問題,這里我就不一一講述了,請讀者參考原書的相關(guān)內(nèi)容,如果仍有疑問,可以聯(lián)系我。17 快樂魔導師Ares.vip.myrice.com3.2實現(xiàn)下面是一般的實現(xiàn)步驟1.獲得計算機信息,得到CPU的數(shù)量。創(chuàng)建一個完成端口,第四個參數(shù)置0,指定NumberOfConcurrentThreads為CPU個數(shù)。2.Determinehowmanyprocessorsexistonthesystem.3.CreateworkerthreadstoservicecompletedI/Orequestsonthecompletionportusingprocessorinformationinstep2.Inthecaseofthissimpleexample,wecreateoneworkerthreadperprocessorbecausewedonotexpectourthreadstoevergetinasuspendedconditioninwhichtherewouldnotbeenoughthreadstoexecuteforeachprocessor.WhentheCreateThreadfunctioniscalled,youmustsupplyaworkerroutinethatthethreadexecutesuponcreation.Wewilldiscusstheworkerthread'sresponsibilitieslaterinthissection.4.Preparealisteningsockettolistenforconnectionsonport5150.5.Acceptinboundconnectionsusingtheacceptfunction.6.Createadatastructuretorepresentper-handledataandsavetheacceptedsockethandleinthestructure.7.AssociatethenewsockethandlereturnedfromacceptwiththecompletionportbycallingCreateIoCompletionPort.Passtheper-handledatastructuretoCreateIoCompletionPortviathecompletionkeyparameter.8.StartprocessingI/Oontheacceptedconnection.Essentially,youwanttopostoneormoreasynchronousWSARecvorWSASendrequestsonthenewsocketusingtheoverlappedI/Omechanism.WhentheseI/Orequestscomplete,aworkerthreadservicestheI/OrequestsandcontinuesprocessingfutureI/Orequests,aswewillseelaterintheworkerroutinespecifiedinstep3.9.Repeatsteps5–8untilserverterminates.那么學習完成端口編程從哪里開始比較好,對于初學者而言,直接進入編程并不是一個好主意,我建議初學者首先學習用異步Socket模式,即WSAEventSelect模式構(gòu)建一個簡單的聊天服務器。當把Windows網(wǎng)絡(luò)編程的概念有一個清晰的認識之后,再深入研究完成端口編程。接著就是深入研究具體的編程實現(xiàn)了,從NetworkProgrammingforMicrosoftWindows2nd中摘錄的這段經(jīng)典代碼可以說是非常合適的,這里我只簡單解釋一下其中比較關(guān)鍵的地方,還有不明白的可以參看原書,或者聯(lián)系我。主程序段:1.HANDLECompletionPort;2.WSADATAwsd;3.SYSTEM_INFOSystemInfo;17 快樂魔導師Ares.vip.myrice.com1.SOCKADDR_INInternetAddr;2.SOCKETListen;3.inti;4.5.typedefstruct_PER_HANDLE_DATA6.{7.SOCKETSocket;8.SOCKADDR_STORAGEClientAddr;9.//在這里還可以加入其他和客戶端關(guān)聯(lián)的數(shù)據(jù)10.}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;11.12.//初始化WindowsSocket2.213.StartWinsock(MAKEWORD(2,2),&wsd);14.15.//Step1:16.//創(chuàng)建完成端口17.18.CompletionPort=CreateIoCompletionPort(19.INVALID_HANDLE_VALUE,NULL,0,0);20.21.//Step2:22.//檢測系統(tǒng)信息23.24.GetSystemInfo(&SystemInfo);25.26.//Step3:創(chuàng)建工作者線程,數(shù)量和CPU的數(shù)量一樣多27.//Createworkerthreadsbasedonthenumberof28.//processorsavailableonthesystem.Forthis29.//simplecase,wecreateoneworkerthreadforeach30.//processor.31.32.for(i=0;iSocket=Accept;43.memcpy(&PerHandleData->ClientAddr,&saRemote,RemoteLen);44.17 快樂魔導師Ares.vip.myrice.com1.//Step7:2.//Associatetheacceptedsocketwiththe3.//completionport4.5.CreateIoCompletionPort((HANDLE)Accept,6.CompletionPort,(DWORD)PerHandleData,0);7.8.//Step8:發(fā)出對客戶端的I/O請求,等待完成消息9.//StartprocessingI/Oontheacceptedsocket.10.//PostoneormoreWSASend()orWSARecv()calls11.//onthesocketusingoverlappedI/O.12.WSARecv(...);13.}14.15.工作者線程DWORDWINAPIServerWorkerThread(LPVOIDCompletionPortID){HANDLECompletionPort=(HANDLE)CompletionPortID;DWORDBytesTransferred;LPOVERLAPPEDOverlapped;LPPER_HANDLE_DATAPerHandleData;LPPER_IO_DATAPerIoData;DWORDSendBytes,RecvBytes;DWORDFlags;while(TRUE){//等待完成端口消息,未收到消息德時候則阻塞線程ret=GetQueuedCompletionStatus(CompletionPort,&BytesTransferred,(LPDWORD)&PerHandleData,(LPOVERLAPPED*)&PerIoData,INFINITE);//Firstchecktoseeifanerrorhasoccurred//onthesocket;ifso,closethe//socketandcleanuptheper-handledata//andper-I/Ooperationdataassociatedwith//thesocketif(BytesTransferred==0&&17 快樂魔導師Ares.vip.myrice.com(PerIoData->OperationType==RECV_POSTED││PerIoData->OperationType==SEND_POSTED)){//AzeroBytesTransferredindicatesthatthe//sockethasbeenclosedbythepeer,so//youshouldclosethesocket.Note://Per-handledatawasusedtoreferencethe//socketassociatedwiththeI/Ooperation.closesocket(PerHandleData->Socket);GlobalFree(PerHandleData);GlobalFree(PerIoData);continue;}//ServicethecompletedI/Orequest.Youcan//determinewhichI/Orequesthasjust//completedbylookingattheOperationType//fieldcontainedintheper-I/Ooperationdata.if(PerIoData->OperationType==RECV_POSTED){//Dosomethingwiththereceiveddata//inPerIoData->Buffer}//PostanotherWSASendorWSARecvoperation.//Asanexample,wewillpostanotherWSARecv()//I/Ooperation.Flags=0;//Setuptheper-I/Ooperationdataforthenext//overlappedcallZeroMemory(&(PerIoData->Overlapped),sizeof(OVERLAPPED));PerIoData->DataBuf.len=DATA_BUFSIZE;PerIoData->DataBuf.buf=PerIoData->Buffer;PerIoData->OperationType=RECV_POSTED;WSARecv(PerHandleData->Socket,&(PerIoData->DataBuf),1,&RecvBytes,&Flags,&(PerIoData->Overlapped),NULL);17 快樂魔導師Ares.vip.myrice.com}}3.3小節(jié)講這么點就完了?你一定認為我介紹的東西并沒有超過原書中的內(nèi)容,實事上完成端口編程的精髓就是上面的代碼和原書中的有關(guān)敘述。如果我再把他們完整的重復一遍,那又有什么意思呢?根據(jù)我的經(jīng)驗,設(shè)計網(wǎng)絡(luò)服務器的真正難點,不在于完成端口技術(shù),所以我想利用小節(jié)把自己編程中的一些經(jīng)驗告訴大家。首先是服務器的管理,一個服務器首先要分析它的設(shè)計目標是應對很多的連接還是很大的數(shù)據(jù)傳送量。這樣在設(shè)計工作者線程時就可以最大限度的提高性能。管理客戶端方面,我們可以將客戶端的數(shù)據(jù)捆綁到Perhand-Data數(shù)據(jù)結(jié)構(gòu)上,如果還有需要,可以建一個表來記錄客戶端的宏觀情況。在Ares引擎中,我將文件傳送和大容量數(shù)據(jù)傳送功能也封裝進了服務器和客戶端。我建議服務器和客戶端都應該封裝這些功能,盡管我們并不是做FTP服務器,但是當客戶端需要和服務器交換文件和大塊數(shù)據(jù)時,你會發(fā)現(xiàn)這樣做,靈活性和性能都能做得比用單純的FTP協(xié)議來更好,所以在你的服務器和客戶端可以傳送數(shù)據(jù)包以后,把他們都做進去吧。為了服務器不被黑客攻擊,或被BUG弄崩潰,我們還需要認真設(shè)計服務器的認證機制,以及密切注意程序中的溢出,一定要在每一個使用緩沖區(qū)的地方加上檢查代碼??梢哉f并沒有現(xiàn)成的辦法來解決這個問題,不然就沒有人研究網(wǎng)絡(luò)安全了,所以我們要做的是盡量減少錯誤,即使出現(xiàn)錯誤也不會造成太大損失,在發(fā)現(xiàn)錯誤的時候能夠很快糾正同類錯誤。還有就是對客戶端情況的檢測,比如客戶端的正常和非正常斷開連接。如果不注意這一點,就會造成服務器資源持續(xù)消耗而最終崩潰,因為我們的服務器不可能總是重啟,而是要持續(xù)的運行,越久越好。還有比如客戶端斷開連接后又嘗試連接,但是在服務器看來這個客戶“仍然在線“,這個時候我們不能單純的拒絕客戶端的連接,也不能單純的接收。講了幾點服務器設(shè)計中的問題,他們只是眾多問題中的一小部分,限于時間原因,在這個版本的文章中就說這么多。你一定會發(fā)現(xiàn),其實網(wǎng)絡(luò)編程最困難和有成就的地方,并不是服務器用了什么模式等等,而是真正深入設(shè)計的時候碰到的眾多問題。正是那些沒有標準答案的問題,值得我們?nèi)パ芯亢徒鉀Q。第四章作者的話寫這篇文章的目的,一方面是簡要的談談游戲編程中的網(wǎng)絡(luò)部分。另一方面是結(jié)交眾多開發(fā)的朋友。畢竟我們做東西不可能不和他人交流,也不可能只做非商業(yè)化的項目。我開發(fā)的Ares引擎就是同時為了這兩個目的,到我寫這篇文章的時候,引擎的版本仍然是3.2,并不是我不想繼續(xù)開發(fā),也不是沒有新的改變了。恰恰相反,我有很多新的想法,急切想把他們加入新的版本中,只是現(xiàn)在手上還有短期的項目沒有完成。有希望交流的朋友,希望合作開發(fā)的朋友,有項目委托的朋友。。。聯(lián)系我。17 快樂魔導師Ares.vip.myrice.com17

當前文檔最多預覽五頁,下載文檔查看全文

此文檔下載收益歸作者所有

當前文檔最多預覽五頁,下載文檔查看全文
溫馨提示:
1. 部分包含數(shù)學公式或PPT動畫的文件,查看預覽時可能會顯示錯亂或異常,文件下載后無此問題,請放心下載。
2. 本文檔由用戶上傳,版權(quán)歸屬用戶,天天文庫負責整理代發(fā)布。如果您對本文檔版權(quán)有爭議請及時聯(lián)系客服。
3. 下載前請仔細閱讀文檔內(nèi)容,確認文檔內(nèi)容符合您的需求后進行下載,若出現(xiàn)內(nèi)容與標題不符可向本站投訴處理。
4. 下載文檔時可能由于網(wǎng)絡(luò)波動等原因無法下載或下載錯誤,付費完成后未能成功下載的用戶請聯(lián)系客服處理。
大家都在看
近期熱門
關(guān)閉