《VC++程序設(shè)計(jì)》第9講-Winsock網(wǎng)絡(luò)編程_第1頁(yè)
《VC++程序設(shè)計(jì)》第9講-Winsock網(wǎng)絡(luò)編程_第2頁(yè)
《VC++程序設(shè)計(jì)》第9講-Winsock網(wǎng)絡(luò)編程_第3頁(yè)
《VC++程序設(shè)計(jì)》第9講-Winsock網(wǎng)絡(luò)編程_第4頁(yè)
《VC++程序設(shè)計(jì)》第9講-Winsock網(wǎng)絡(luò)編程_第5頁(yè)
已閱讀5頁(yè),還剩75頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第9講WindSock網(wǎng)絡(luò)編程主講教師:李璟e-mail:

第9講WinSock網(wǎng)絡(luò)編程9.1網(wǎng)絡(luò)通信根底9.2WinSock概述9.3MFCWinSock編程9.4MFCWinSock網(wǎng)絡(luò)編程實(shí)例9.1網(wǎng)絡(luò)通信根底網(wǎng)絡(luò)編程是指:應(yīng)用程序需要與網(wǎng)絡(luò)中其它系統(tǒng)上的應(yīng)用程序之間進(jìn)行通信。以下介紹一下有關(guān)網(wǎng)絡(luò)通信的根底知識(shí)。計(jì)算機(jī)網(wǎng)絡(luò)計(jì)算機(jī)網(wǎng)絡(luò)是相互連接的獨(dú)立自主的計(jì)算機(jī)的集合,最簡(jiǎn)單的網(wǎng)絡(luò)形式由兩臺(tái)計(jì)算機(jī)組成。兩臺(tái)計(jì)算機(jī)通過(guò)網(wǎng)絡(luò)進(jìn)行通信AB網(wǎng)絡(luò)協(xié)議協(xié)議端口號(hào)端口號(hào)IP地址IP網(wǎng)絡(luò)中每臺(tái)主機(jī)都必須有一個(gè)惟一的IP地址;IP地址是一個(gè)邏輯地址;因特網(wǎng)上的IP地址具有全球唯一性;32位,4個(gè)字節(jié),常用點(diǎn)分十進(jìn)制的格式表示,例如:協(xié)議為進(jìn)行網(wǎng)絡(luò)中的數(shù)據(jù)交換〔通信〕而建立的規(guī)那么、標(biāo)準(zhǔn)或約定。(=語(yǔ)義+語(yǔ)法+規(guī)那么)不同層具有各自不同的協(xié)議。網(wǎng)絡(luò)的狀況多種通信媒介——有線、無(wú)線……不同種類的設(shè)備——通用、專用……不同的操作系統(tǒng)——Unix、Windows……不同的應(yīng)用環(huán)境——固定、移動(dòng)……

它們互相交織,形成了非常復(fù)雜的系統(tǒng)應(yīng)用環(huán)境。網(wǎng)絡(luò)異質(zhì)性問(wèn)題的解決網(wǎng)絡(luò)體系結(jié)構(gòu)就是使這些用不同媒介連接起來(lái)的不同設(shè)備和網(wǎng)絡(luò)系統(tǒng)在不同的應(yīng)用環(huán)境下實(shí)現(xiàn)互操作性,并滿足各種業(yè)務(wù)需求的一種粘合劑,它營(yíng)造了一種“生存空間”——任何廠商的任何產(chǎn)品、以及任何技術(shù)只要遵守這個(gè)空間的行為規(guī)那么,就能夠在其中生存并開(kāi)展。網(wǎng)絡(luò)體系結(jié)構(gòu)解決異質(zhì)性問(wèn)題采用的是分層方法——把復(fù)雜的網(wǎng)絡(luò)互聯(lián)問(wèn)題劃分為假設(shè)干個(gè)較小的、單一的問(wèn)題,在不同層上予以解決。 就像我們?cè)诰幊虝r(shí)把問(wèn)題分解為很多小的模塊來(lái)解決一樣。ISO/OSI七層參考模型OSI(OpenSystemInterconnection)參考模型將網(wǎng)絡(luò)的不同功能劃分為7層。應(yīng)用層表示層物理層會(huì)話層傳輸層網(wǎng)絡(luò)層數(shù)據(jù)鏈路層處理網(wǎng)絡(luò)應(yīng)用數(shù)據(jù)表示主機(jī)間通信端到端的連接尋址和最短路徑介質(zhì)訪問(wèn)(接入)二進(jìn)制傳輸ISO/OSI七層參考模型通信實(shí)體的對(duì)等層之間不允許直接通信。各層之間是嚴(yán)格單向依賴。上層使用下層提供的效勞—Serviceuser;下層向上層提供效勞—Serviceprovider。對(duì)等通信例如“你好”“Hello”

中國(guó)教師翻譯秘書“Hallo”“Hello”

德國(guó)教師翻譯秘書對(duì)交談內(nèi)容的共識(shí)用英語(yǔ)對(duì)話使用通信物理通信線路對(duì)等層通信的實(shí)質(zhì)對(duì)等層實(shí)體之間虛擬通信。下層向上層提供效勞,實(shí)際通信在最底層完成。OSI各層所使用的協(xié)議應(yīng)用層:遠(yuǎn)程登錄協(xié)議Telnet、文件傳輸協(xié)議FTP、超文本傳輸協(xié)議HTTP、域名效勞DNS、簡(jiǎn)單郵件傳輸協(xié)議SMTP、郵局協(xié)議POP3等。傳輸層:傳輸控制協(xié)議TCP、用戶數(shù)據(jù)報(bào)協(xié)議UDP。TCP:面向連接的可靠的傳輸協(xié)議。UDP:是無(wú)連接的,不可靠的傳輸協(xié)議。網(wǎng)絡(luò)層:網(wǎng)際協(xié)議IP、Internet互聯(lián)網(wǎng)控制報(bào)文協(xié)議ICMP、Internet組管理協(xié)議IGMP。數(shù)據(jù)封裝一臺(tái)計(jì)算機(jī)要發(fā)送數(shù)據(jù)到另一臺(tái)計(jì)算機(jī),數(shù)據(jù)首先必須打包,打包的過(guò)程稱為封裝。封裝就是在數(shù)據(jù)前面加上特定的協(xié)議頭部。數(shù)據(jù)數(shù)據(jù)協(xié)議頭數(shù)據(jù)封裝OSI參考模型中,對(duì)等層協(xié)議之間交換的信息單元統(tǒng)稱為協(xié)議數(shù)據(jù)單元(PDU,ProtocolDataUnit)。OSI參考模型中每一層都要依靠下一層提供的效勞。為了提供效勞,下層把上層的PDU作為本層的數(shù)據(jù)封裝,然后參加本層的頭部〔和尾部〕。頭部中含有完成數(shù)據(jù)傳輸所需的控制信息。這樣,數(shù)據(jù)自上而下遞交的過(guò)程實(shí)際上就是不斷封裝的過(guò)程。到達(dá)目的地后自下而上遞交的過(guò)程就是不斷拆封的過(guò)程。由此可知,在物理線路上傳輸?shù)臄?shù)據(jù),其外面實(shí)際上被包封了多層“信封”。但是,某一層只能識(shí)別由對(duì)等層封裝的“信封”,而對(duì)于被封裝在“信封”內(nèi)部的數(shù)據(jù)僅僅是拆封后將其提交給上層,本層不作任何處理。TCP/IP模型TCP/IP起源于美國(guó)國(guó)防部高級(jí)研究規(guī)劃署(DARPA)的一項(xiàng)研究方案——實(shí)現(xiàn)假設(shè)干臺(tái)主機(jī)的相互通信。現(xiàn)在TCP/IP已成為Internet上通信的工業(yè)標(biāo)準(zhǔn)。TCP/IP模型包括4個(gè)層次:應(yīng)用層傳輸層網(wǎng)絡(luò)層網(wǎng)絡(luò)接口TCP/IP與OSI參考模型的對(duì)應(yīng)關(guān)系應(yīng)用層表示層會(huì)話層傳輸層物理層數(shù)據(jù)鏈路層網(wǎng)絡(luò)層7654321OSI參考模型應(yīng)用層傳輸層網(wǎng)絡(luò)接口網(wǎng)絡(luò)層TCP/IP模型端口按照OSI七層模型的描述,傳輸層提供進(jìn)程〔應(yīng)用程序〕通信的能力。為了標(biāo)識(shí)通信實(shí)體中進(jìn)行通信的進(jìn)程〔應(yīng)用程序〕,TCP/IP協(xié)議提出了協(xié)議端口〔protocolport,簡(jiǎn)稱端口〕的概念。端口是一種抽象的軟件結(jié)構(gòu)〔包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū)〕。應(yīng)用程序通過(guò)系統(tǒng)調(diào)用與某端口建立連接〔binding〕后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)的進(jìn)程所接收,相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)都通過(guò)該端口輸出。端口用一個(gè)整數(shù)型標(biāo)識(shí)符來(lái)表示,即端口號(hào)。端口號(hào)跟協(xié)議相關(guān),TCP/IP傳輸層的兩個(gè)協(xié)議TCP和UDP是完全獨(dú)立的兩個(gè)軟件模塊,因此各自的端口號(hào)也相互獨(dú)立。端口使用一個(gè)16位的數(shù)字來(lái)表示,它的范圍是0~65535,1024以下的端口號(hào)保存給預(yù)定義的效勞。例如:使用80端口。9.2WinSock概述WindowsSock〔Windows套接字,簡(jiǎn)稱WinSock〕是微軟根據(jù)UNIX操作系統(tǒng)中流行的Berkeley(伯克利)套接字標(biāo)準(zhǔn),而實(shí)現(xiàn)的一套MicrosoftWindows下的網(wǎng)絡(luò)編程接口。在ISO的OSI網(wǎng)絡(luò)七層協(xié)議中,WinSock主要負(fù)責(zé)控制數(shù)據(jù)的輸入和輸出,也就是傳輸層和網(wǎng)絡(luò)層。它屏蔽了數(shù)據(jù)鏈路層和物理層,給Windows下的網(wǎng)絡(luò)編程帶來(lái)了巨大的變化。WindowsSockets通信機(jī)制WindowsSockets通信的根底是套接字(Socket)。Socket就是在應(yīng)用程序之間用于讀〔接收信息〕或?qū)憽舶l(fā)送信息〕的一個(gè)網(wǎng)絡(luò)對(duì)象??蛻魴C(jī)/效勞器模式在TCP/IP網(wǎng)絡(luò)應(yīng)用中,通信的兩個(gè)進(jìn)程間相互作用的主要模式是客戶機(jī)/效勞器模式(client/server),即客戶向效勞器提出請(qǐng)求,效勞器接收到請(qǐng)求后,提供相應(yīng)的效勞。客戶機(jī)/效勞器模式客戶機(jī)/效勞器模式在操作過(guò)程中采取的是主動(dòng)請(qǐng)求的方式。首先效勞器方要先啟動(dòng),并根據(jù)請(qǐng)求提供相應(yīng)的效勞:①翻開(kāi)一個(gè)通信通道并告知本地主機(jī),它愿意在某一地址和端口上接收客戶請(qǐng)求。②等待客戶請(qǐng)求到達(dá)該端口。③接收到效勞請(qǐng)求,處理該請(qǐng)求并發(fā)送應(yīng)答信號(hào),同時(shí)要激活一個(gè)新的進(jìn)程(或線程)來(lái)處理這個(gè)客戶請(qǐng)求。新進(jìn)程(或線程)處理此客戶請(qǐng)求,并不需要對(duì)其它請(qǐng)求作出應(yīng)答。效勞完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。④返回第二步,等待另一客戶請(qǐng)求。⑤關(guān)閉效勞器??蛻舴剑孩俜_(kāi)一個(gè)通信通道,并連接到效勞器所在主機(jī)的特定端口。②向效勞器發(fā)效勞請(qǐng)求報(bào)文,等待并接收應(yīng)答;繼續(xù)提出請(qǐng)求。③請(qǐng)求結(jié)束后關(guān)閉通信通道并終止?;赥CP(面向連接)的socket編程效勞器端程序:1、創(chuàng)立套接字〔socket〕。 2、將套接字綁定到一個(gè)本地地址和端口上〔bind〕。3、將套接字設(shè)為監(jiān)聽(tīng)模式,準(zhǔn)備接收客戶請(qǐng)求〔listen〕。4、等待客戶請(qǐng)求到來(lái);當(dāng)請(qǐng)求到來(lái)后,接受連接請(qǐng)求,返回一個(gè)新的對(duì)應(yīng)于此次連接的套接字〔accept〕。5、用返回的套接字和客戶端進(jìn)行通信〔send/receive〕。6、返回第4步,等待另一客戶請(qǐng)求。7、關(guān)閉套接字??蛻舳顺绦颍?、創(chuàng)立套接字〔socket〕。 2、向效勞器發(fā)出連接請(qǐng)求〔connect〕。3、和效勞器端進(jìn)行通信〔send/receive〕。4、關(guān)閉套接字。9.3MFCWinSock編程MFC為套接字提供了相應(yīng)的類CAsynSocket和CSocket,它們封裝了WindowsSockets的API,從而程序員可以用面向?qū)ο蟮姆椒ㄕ{(diào)用Socket。根本類CAsySocket提供了全面的事件驅(qū)動(dòng)的Socket通信能力,用戶可以創(chuàng)立自己派生的Socket類,來(lái)捕獲和響應(yīng)每個(gè)事件。CSocket類是CAsySocket的派生類,封裝和簡(jiǎn)化了根本類的某些功能。9.3MFCWinSock編程9.3.1MFCWinSock類初始化的創(chuàng)立、連接與關(guān)閉數(shù)據(jù)的發(fā)送與接收9.3.1MFCWinSock類CAsyncSocket類是在一個(gè)很低的層次上封裝了WindowsSocketsAPI,只提供了一些根本的操作。CSocket那么是由CAsyncSocket派生,是WindowsSocketsAPI的高層抽象,提供了更高層次的功能。通過(guò)MFCWinSock類,開(kāi)發(fā)者可以不考慮網(wǎng)絡(luò)字節(jié)順序和忽略掉更多的通信細(xì)節(jié)。CAsyncSocket類主要成員函數(shù)及其功能〔一〕構(gòu)造函數(shù)CAsyncSocket:構(gòu)造CAsyncSocket對(duì)象。Create:創(chuàng)立套接字。屬性:Attach:對(duì)CAsyncSocket對(duì)象附加套接字句柄。Detach:從CAsyncSocket對(duì)象除去套接字句柄。FromHandle:返回CAsyncSocket對(duì)象的指針,給出套接字句柄。GetLastError:獲得上一次運(yùn)行失敗的狀態(tài)。GetPeerName:獲得與套接字連接的對(duì)等套接字的地址。GetSockName:獲得套接字的本地名。GetSockOpt:獲得套接字選項(xiàng)。SetSockOpt:設(shè)置套接字選項(xiàng)。CAsyncSocket類主要成員函數(shù)及其功能〔二〕操作函數(shù):Accept:接受套接字上的連接請(qǐng)求。CAsyncSelect:請(qǐng)求對(duì)于套接字的事件通知。Bind:與套接字有關(guān)的本地地址。Close:關(guān)閉套接字。Connect:發(fā)出連接請(qǐng)求。IOCtr:控制套接字模式。Listen:建立套接字,監(jiān)聽(tīng)即將到來(lái)的連接請(qǐng)求。Receive:從套接字上接收數(shù)據(jù)。Send:給連接套接字發(fā)送數(shù)據(jù)。SendTo:給特定目的地發(fā)送數(shù)據(jù)。ShutDown:使套接字上的send或receive調(diào)用無(wú)效。CAsyncSocket類主要成員函數(shù)及其功能〔三〕可重載函數(shù):OnAccept:通知偵聽(tīng)套接字,通過(guò)Accept接受連接請(qǐng)求。OnClose:通知套接字,關(guān)閉對(duì)它的套接字連接。OnConnect:通知連接套接字,連接嘗試已經(jīng)完成,無(wú)論成功或失敗。OnReceive:通知偵聽(tīng)套接字,通過(guò)調(diào)用Receive接收數(shù)據(jù)。OnSend:通知套接字,通過(guò)調(diào)用Send,它可以發(fā)送數(shù)據(jù)。數(shù)據(jù)成員:m_hSocket:指定附加在此AsyncSocket對(duì)象上的SOCKET句柄。CSocket類主要成員及其功能構(gòu)造函數(shù):CSocket:構(gòu)造一個(gè)CSocket對(duì)象。Create:創(chuàng)立一個(gè)套接字。屬性:IsBlocking:確定一個(gè)阻塞調(diào)用是否在進(jìn)行中。FromHandle:返回一個(gè)指向CSocket對(duì)象的指針,給出一個(gè)套接字句柄。操作函數(shù):CancelBlockingCall:取消一個(gè)當(dāng)前在進(jìn)行中的套接字調(diào)用??芍剌d函數(shù)OnMessagePending:當(dāng)?shù)却瓿梢粋€(gè)阻塞調(diào)用時(shí)調(diào)用此函數(shù)來(lái)處理懸而未決的消息。初始化在使用WinSockMFC類之前,必須為應(yīng)用程序初始化WinSock環(huán)境。這只需要調(diào)用實(shí)例初始化函數(shù)AfxSocketInit()即可。如下面的代碼:BOOLCChatRoomServerApp::InitInstance(){ if(!AfxSocketInit()) { AfxMessageBox(IDP_SOCKETS_INIT_FAILED); returnFALSE; }}同時(shí),在”stdafx.h”文件中添加如下代碼:#include<afxsock.h>如果在使用MFCAppWizard[EXE]創(chuàng)立MFC工程時(shí),在MFCAppWizardstep2對(duì)話框選擇”WindowsSockets”選項(xiàng),那么程序會(huì)自動(dòng)添加上面的代碼,實(shí)現(xiàn)WinSock的初始化。的創(chuàng)立、連接與關(guān)閉初始化Winsock環(huán)境后,即可以實(shí)現(xiàn)socket的創(chuàng)立、連接與關(guān)閉。1.創(chuàng)立Socket為了使應(yīng)用程序可以使用Socket,首先需要聲明CAsyncSocket、CSocket或者這兩個(gè)類派生類的一個(gè)對(duì)象成員,如:CAsyncSocketm_sock;使用Socket對(duì)象之前,必須調(diào)用它的Create函數(shù)創(chuàng)立Socket。如果準(zhǔn)備用Socket連接另一個(gè)應(yīng)用程序,作為客戶程序,不必給Create函數(shù)傳送任何參數(shù)。而如果Socket作為效勞程序,準(zhǔn)備監(jiān)聽(tīng)另一個(gè)應(yīng)用程序的連接請(qǐng)求,那么至少需要傳送一個(gè)端口給Create函數(shù):if(m_sock.Create(5800)){……//Socket創(chuàng)立成功后的代碼}else……//錯(cuò)誤處理代碼Create函數(shù)的其他參數(shù)還有很多,可以包括Socket類型、準(zhǔn)備響應(yīng)的事件以及Socket的本地的IP地址等,一般情況下取默認(rèn)值即可。2.Socket連接建立Socket對(duì)象后,就可以創(chuàng)立一個(gè)連接,共需要以下3個(gè)步驟:(1)在效勞器端讓Socket調(diào)用listen()函數(shù),監(jiān)聽(tīng)對(duì)方的連接請(qǐng)求。(2)在客戶端通過(guò)調(diào)用Connect()函數(shù)連接效勞器。(3)在效勞器通過(guò)調(diào)用Accept()函數(shù)接受連接請(qǐng)求。(1)在效勞器端讓Socket調(diào)用listen()函數(shù),監(jiān)聽(tīng)對(duì)方的連接請(qǐng)求。listen()函數(shù)的調(diào)用方式如下:if(m_sock.listen()){……//Socket監(jiān)聽(tīng)成功后的代碼}else……//錯(cuò)誤處理代碼(2)在客戶端通過(guò)調(diào)用Connect()函數(shù)連接效勞器。使用Connect()函數(shù)連接效勞器必須傳達(dá)兩個(gè)參數(shù):連接的計(jì)算機(jī)名或IP地址和端口號(hào)。Connect()函數(shù)的調(diào)用方式如下:if(m_sock.Connect(“”,5802));{……//Socket連接成功后的代碼}else……//錯(cuò)誤處理代碼(3)一旦效勞器監(jiān)聽(tīng)到連接請(qǐng)求,CAsycnSocket類就激活OnAccept()事件,讓應(yīng)用程序知道已有連接請(qǐng)求,那么效勞器端必須調(diào)用Accept()函數(shù)接受連接請(qǐng)求。Accept()函數(shù)調(diào)用成功,將創(chuàng)立另一個(gè)Socket,通過(guò)它與對(duì)方應(yīng)用程序連接。Accept()函數(shù)的調(diào)用方式如下:if(m_sock.Accept(m_sock2));//等待連接請(qǐng)求{……//連接請(qǐng)求成功后的代碼}else……//連接請(qǐng)求失敗后的代碼m_sock2和客戶方建立了連接,以后就通過(guò)這個(gè)m_sock2對(duì)象去和客戶方進(jìn)行通信,而監(jiān)聽(tīng)Socketm_sock仍然繼續(xù)在監(jiān)聽(tīng),一旦又有一個(gè)客戶方要連接效勞方,OnAccept()又會(huì)被調(diào)用一次。m_sock2是和客戶方通信的效勞方,它不會(huì)觸發(fā)OnAccept()事件,因?yàn)樗皇潜O(jiān)聽(tīng)Socket。3.關(guān)閉Socket連接在應(yīng)用程序之間的通信完成之后,就可以調(diào)用Close()函數(shù)關(guān)閉這個(gè)連接。如下:m_sock.close();數(shù)據(jù)的發(fā)送與接收通過(guò)Socket提供的send()和Receive()函數(shù)可以實(shí)現(xiàn)任何類型數(shù)據(jù)的發(fā)送和接收。1.發(fā)送通過(guò)Socket連接發(fā)送請(qǐng)求,可以使用Send()函數(shù),該函數(shù)的原型如下:intCAsynSocket::Send(constvoid*lpBuf,intnBufLen,intnFlags=0);各參數(shù)含義如下:lpBuf:指向發(fā)送數(shù)據(jù)緩沖區(qū)的指針。數(shù)據(jù)為CString變量時(shí),可使用LPCTSTR操作符把CString變量作為緩沖區(qū)傳送。nBuflen:指明緩沖區(qū)要發(fā)送數(shù)據(jù)的長(zhǎng)度。nFlags:該參數(shù)是可選的,用于控制消息的發(fā)送方式。函數(shù)執(zhí)行成功,返回發(fā)送到對(duì)方應(yīng)用程序的數(shù)據(jù)總量。如果有錯(cuò)誤產(chǎn)生,函數(shù)返回SOCKET_ERROR。典型的Send()函數(shù)調(diào)用方式如下:charstr[1000];intilen;intiSend;iLen=str.GetLength();iSend=m_sock.Send(LPCTSTR(str),iLen);if(iSend==SOCKET_ERROR){……//發(fā)送不成功錯(cuò)誤處理代碼}else{……//發(fā)送成功處理代碼}2.接收數(shù)據(jù)發(fā)送數(shù)據(jù)后,在另一端應(yīng)用程序就可通過(guò)Receive()函數(shù)接收數(shù)據(jù)了。其函數(shù)原型如下:intCAsyncSocket::Receive(void*lpBuf,intnBufLen,intnFlags=0);各參數(shù)含義如下:lpBuf:指向接收數(shù)據(jù)緩沖區(qū)的指針。nBufLen:接收數(shù)據(jù)緩沖區(qū)的長(zhǎng)度。nFlags:該參數(shù)是可選的,用于控制消息的發(fā)送方式。通過(guò)執(zhí)行成功后,Receive()函數(shù)也返回接收到的數(shù)據(jù)的數(shù)據(jù)量。如果有錯(cuò)誤產(chǎn)生,函數(shù)返回SOCKET_ERROR。典型的Receive()的調(diào)用方式如下:charBuf[1024];intiRecv;iRecv=m_sock.Receive(Buff,1024);if(iRecv==SOCKET_ERROR){//接收不成功錯(cuò)誤處理代碼}else{Buf[iRecv]=0;接收成功時(shí)處理代碼……}9.4MFCWinSock網(wǎng)絡(luò)編程實(shí)例為了說(shuō)明WinSock的根本功能,本節(jié)將采用MFC提供的CSocket類實(shí)現(xiàn)一個(gè)采用Client/Server結(jié)構(gòu)的可以多人同時(shí)在線的聊天室。正如大多數(shù)聊天室一樣,需要一個(gè)聊天室效勞器,它可以和很多客戶端進(jìn)行通信,從而實(shí)現(xiàn)把來(lái)自不同的客戶的聊天信息轉(zhuǎn)發(fā)給所有其他的客戶端。9.4MFCWinSock網(wǎng)絡(luò)編程實(shí)例實(shí)例說(shuō)明效勞器端程序創(chuàng)立客戶端程序創(chuàng)立實(shí)例說(shuō)明創(chuàng)立一個(gè)多人在線的效勞器,首先必須有一個(gè)效勞器端口程序,各客戶端消息均發(fā)送至效勞器,而效勞器那么將收到的消息分發(fā)給所有的客戶程序。對(duì)于本例,效勞器和客戶端均在本機(jī)運(yùn)行,因此效勞器的IP地址為。效勞器程序?yàn)橐粋€(gè)簡(jiǎn)單的對(duì)話框程序,只有3個(gè)按鈕:“啟動(dòng)”、“停止”、“退出”,分別實(shí)現(xiàn)啟動(dòng)、停止網(wǎng)絡(luò)效勞和退出窗口的功能。如以下圖所示:客戶端啟動(dòng)時(shí),首先彈出窗口實(shí)現(xiàn)用戶的連接,就可以向效勞器發(fā)送消息,并在列表框中顯示效勞器會(huì)傳的其所有接收到的消息。其運(yùn)行結(jié)果如以下圖所示:分析:為了實(shí)現(xiàn)多人在線聊天,本程序中設(shè)計(jì)了客戶套接字列表來(lái)保存所有的客戶端套接字。效勞器端程序創(chuàng)立效勞器端程序?yàn)榛趯?duì)話框的MFC應(yīng)用程序。其創(chuàng)立過(guò)程如下:1.創(chuàng)立工程:創(chuàng)立一個(gè)名為”ChatRoomServer”的基于對(duì)話框的MFC工程,在步驟2時(shí)選中“WindowsSockets”選項(xiàng),其他步驟使用默認(rèn)值。2.添加控件資源效勞器端對(duì)話框只有3個(gè)按鈕控件:“啟動(dòng)”、“停止”和“退出”,利用ClassWizard為“啟動(dòng)”和“停止”按鈕添加BN_CLICKED消息響應(yīng)函數(shù)OnButtonStart()和OnButtonStop()。3.派生CSocket類程序從CSocket類派生了兩個(gè)Socket類——CListenSocket和ClientSocket,用于創(chuàng)立監(jiān)聽(tīng)套接字和客戶套接字。其中,ClientSocket類主要用于創(chuàng)立效勞方的客戶端套接字列表,實(shí)現(xiàn)將效勞器接收的消息分發(fā)給所有客戶端Socket。ClistenSocket類主要是重載了CSocket類的OnAccept()函數(shù),用于創(chuàng)立客戶端套接字和維護(hù)客戶端套接字列表?!?〕CClientSocket類該類主要用于創(chuàng)立效勞器客戶Socket,實(shí)現(xiàn)接收消息并分發(fā)給所有連接至效勞器的客戶Socket。CClientSocket類的聲明如下:classCClientSocket:publicCSocket{public: CClientSocket(CClientSocketList*); virtual~CClientSocket(); CClientSocketList*List;//所在列表首地址 CClientSocket*Front;//指向前一個(gè)套接字 CClientSocket*Next;//指向后一個(gè)套接字 virtualvoidOnReceive(intnErrorCode); virtualvoidOnClose(intnErrorCode);};相應(yīng)的函數(shù)實(shí)現(xiàn)代碼如下:CClientSocket::CClientSocket(CClientSocketList*tmp){ Front=0; Next=0; List=tmp;}//此處OnReceice是接收時(shí)激活的。voidCClientSocket::OnReceive(intnErrorCode){//通過(guò)this指針指向的客戶套接字接收數(shù)據(jù)//并把接收到的數(shù)據(jù)再發(fā)送給所有客戶端 List->Sends(this);}〔2〕CClientSocketList類該類用于接收數(shù)據(jù)和向各客戶套接字發(fā)送數(shù)據(jù)。其聲明如下:classCClientSocketList{public:CClientSocketList(); virtual~CClientSocketList();//接收并向所有客戶套接字發(fā)送數(shù)據(jù) BOOLSends(CClientSocket*);//向客戶套接字列表添加一個(gè)套接字 BOOLAdd(CClientSocket*); CClientSocket*Head;//客戶套接字列表元素指針};其主要函數(shù)的實(shí)現(xiàn)如下所示:Add方法//功能:將add元素添加在列表最后。BOOLCClientSocketList::Add(CClientSocket*add){ //tmp暫存Head元素CClientSocket*tmp=Head; if(!Head)//如果列表為空,那么添加頭元素后返回 { Head=add; returntrue; }//假設(shè)列表不為空,那么add元素添加在列表最后, //并把原來(lái)的末尾元素指向該元素while(tmp->Next)tmp=tmp->Next; tmp->Next=add; returntrue;}sends方法//功能:通過(guò)tmp指針指向的客戶套接字接收數(shù)據(jù)//并把接收的數(shù)據(jù)再發(fā)送給所有客戶端BOOLCClientSocketList::Sends(CClientSocket*tmp){ charbuff[1000];//分配緩存 intn;//將當(dāng)前套接字指針置為客戶套接字列表的頭指針 CClientSocket*curr=Head;//把tmp客戶套接字中的內(nèi)容接收到緩存buff中。 n=tmp->Receive(buff,1000); buff[n]=0;//把緩存中buff中的內(nèi)容依次發(fā)送給列表中的所有套接字 while(curr) {curr->Send(buff,n);//向各個(gè)客戶端發(fā)所有聊天記錄 curr=curr->Next; } returntrue;}〔3〕CListenSocket類CListenSocket類派生自CSocket類,用于監(jiān)聽(tīng)套接字接受連接請(qǐng)求,創(chuàng)立客戶端套接字,并把客戶端套接字添加到客戶套接字列表中。其類的聲明如下:classCListenSocket:publicCSocket{public: CListenSocket(); virtual~CListenSocket(); virtualvoidOnAccept(intnErrorCode); CClientSocketListCCSL;};在CListenSocket類中,重載了CSocket::OnAccept函數(shù),用于接受客戶端連接請(qǐng)求,創(chuàng)立與該客戶端通信的套接字,并把該套接字添加到客戶端套接字列表中。OnAccept函數(shù)重新定義如下:voidCListenSocket::OnAccept(intnErrorCode){ //創(chuàng)立客戶套接字并放在客戶端套接字列表中ClientSocket*tmp=newCClientSocket(&CCSL);//Accept()函數(shù)一旦調(diào)用成功,將指定tmp為//新的客戶端套接字,與新客戶端建立通信連接。 Accept(*tmp);//向客戶端套接字列表添加該套接字 CCSL.Add(tmp);}4.添加按鈕響應(yīng)函數(shù)的實(shí)現(xiàn)代碼“開(kāi)始”按鈕響應(yīng)函數(shù)的實(shí)現(xiàn)代碼如下:voidCChatRoomServerDlg::OnButtonStart(){ //使啟動(dòng)按鈕無(wú)效 m_IDC_BUTTON_START.EnableWindow(FALSE);//創(chuàng)立監(jiān)聽(tīng)套接字的端口為6767ListenSocket.Create(6767); ListenSocket.Listen();//開(kāi)始監(jiān)聽(tīng)//將停止按鈕激活 m_IDC_BUTTON_STOP.EnableWindow(TRUE);}“停止”按鈕響應(yīng)函數(shù)的實(shí)現(xiàn)代碼如下:voidCChatRoomServerDlg::OnButtonStop(){

//TODO:Addyourcontrolnotificationhandlercodehere//使停止按鈕無(wú)效 m_IDC_BUTTON_STOP.EnableWindow(FALSE); ListenSocket.Close();//關(guān)閉監(jiān)聽(tīng)套接字 //將啟動(dòng)按鈕激活m_IDC_BUTTON_START.EnableWindow(TRUE);}客戶端程序創(chuàng)立客戶端同樣為基于對(duì)話框的MFC應(yīng)用程序,其創(chuàng)立過(guò)程如下:1.創(chuàng)立工程創(chuàng)立一個(gè)名為“ChatRoomClient”的基于對(duì)話框的MFC工程,同樣在步驟2時(shí)選中“WindowsSockets”選項(xiàng),其他步驟使用默認(rèn)值。2.創(chuàng)立并設(shè)計(jì)一個(gè)派生自CSocket類的CClientSocket類,管理創(chuàng)立客戶端的套接字,以發(fā)送連接請(qǐng)求,和接收數(shù)據(jù)。CClientSocket類的類聲明如下:classCClientSocket:publicCSocket{public: CChatRoomClientDlg*myDlg; BOOLSetDlg(CChatRoomClientDlg*tmp); CStringNikeName; CClientSocket(); virtual~CClientSocket(); virtualvoidOnReceive(intnErrorCode);};類實(shí)現(xiàn)代碼如下:CClientSocket::CClientSocket(){ NikeName=""; myDlg=0;}//設(shè)置套接字的當(dāng)前窗口BOOLCClientSocket::SetDlg(CChatRoomClientDlg*tmp){ myDlg=tmp; returntrue;}voidCClientSocket::OnReceive(intnErrorCode){ myDlg->GetMessage();}3.添加設(shè)計(jì)對(duì)話框資源??蛻舳顺绦蛴袃蓚€(gè)窗口,一個(gè)是登錄窗口,需要輸入一些登錄信息,另外就是聊天主窗口。聊天主窗口布局如以下圖所示:控件類型及各自IDtab順序控件類型控件ID1ButtonIDOK2ListBoxIDC_LIST_CHATBOX3EditIDC_EDIT_MESSAGE4ButtonIDC_BUTTON_SEND5StaticIDC_STATIC_NIKENAME6StaticIDC_STATIC利用ClassWizard,為列表控件CDC_LIST_CHATBOX添加控制變量m_IDC_LIST_CHARBOX,用于顯示聊天信息。為靜態(tài)文本控件IDC_STAIC_NIKENAME增加控制變量m_IDC_STAIC_NIKENAME,用于動(dòng)態(tài)顯示用戶別名。為IDC_EDIT_MESSAGE控件增加成員變量m_IDC_EDIT_MESSAGE,記錄用戶的發(fā)言。登錄窗口是通過(guò)對(duì)話框資源添加進(jìn)去的,其布局如以下圖所示。125436登錄窗口各控件類型及各自IDtab順序控件類型控件ID1ButtonIDOK2ButtonIDCCANCEL3StaticIDC_STATIC_NIKENAME4StaticIDC_STATIC_ADDRESS5EditIDC_EDIT_NIKENAME6EditIDC_EDIT_ADDRESS登錄窗口的類名為CConectDlg,利用“建立類向?qū)А保瑸殛欠Q編輯框控件添加CString類型的成員變量m_IDC_EDIT_NIKENAME。為效勞器地址編輯框控件IDC_EDIT_ADDRESS添加CString類型的成員變量m_IDC_ADDRESS。4.登錄對(duì)話框的支持類CConectDlg登錄對(duì)話框類中要實(shí)現(xiàn)與效勞器的連接。由于要完成與效勞器的連接操作必然離不開(kāi)Socket,而為了在登錄對(duì)話框銷毀后,能夠繼續(xù)使用與效勞器連接的Socket,因此在CConectDlg類的聲明里直接定義了Socket指針形參,以把Socket傳回。類的聲明如下:類的聲明如下:classCConnectedDlg:publicCDialog{public:CConnectedDlg(CClientSocket*tmp,CWnd*pParent=NULL);CClientSocket*myServerSocket;……};類的實(shí)現(xiàn)如下:CConnectedDlg::CConnectedDlg(CClientSocket*tmp,CWnd*pParent/*=NULL*/):CDialog(CConnectedDlg::IDD,pParent){ …… myServerSocket=tmp;}而“登錄”按鈕的響應(yīng)函數(shù)OnOK()的實(shí)現(xiàn)代碼如下:voidCConnectedDlg::OnOK(){ //TODO:Addextravalidationhere UpdateData(TRUE);//控件->數(shù)據(jù)成員 char*nikename,*address; intn; if(!myServerSocket->Create()) {myServerSocket->Close(); AfxMessageBox("網(wǎng)絡(luò)創(chuàng)立錯(cuò)誤?。?); return; }

n=m_IDC_EDIT_ADDRESS.GetLength(); address=newchar(n+1);//sprintf將內(nèi)容格式化為字符串保存在address中 sprintf(address,"%s",m_IDC_EDIT_ADDRESS.GetBuffer(n)); address[n]=0; n=m_IDC_EDIT_NIKENAME.GetLength(); nikename=newchar(n+1); sprintf(nikename,"%s",m_IDC_EDIT_NIKENAME.GetBuffer(n)); nikename[n]=0;//向效勞器6767端口請(qǐng)求連接。 if(!myServerSocket->Connect(address,6767)) {myServerSocket->Close(); AfxMessageBox

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論