• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天行健 君子當(dāng)自強(qiáng)而不息

            使用DirectPlay進(jìn)行網(wǎng)絡(luò)互聯(lián)(1)


            了解網(wǎng)絡(luò)互聯(lián)

            網(wǎng)絡(luò)是指多臺(tái)計(jì)算機(jī)互聯(lián)以進(jìn)行數(shù)據(jù)傳輸及通信的系統(tǒng)。除了兩個(gè)或更多的計(jì)算機(jī)之外,網(wǎng)路還需要有網(wǎng)絡(luò)互聯(lián)軟件(或一個(gè)網(wǎng)路操作系統(tǒng))、網(wǎng)絡(luò)適配器以及電纜。網(wǎng)絡(luò)適配器有各種形狀和大小,但是一般都采用調(diào)制解調(diào)器的形狀。實(shí)際上,調(diào)制解調(diào)器就是一個(gè)網(wǎng)路適配器,它能夠?qū)⒁慌_(tái)計(jì)算機(jī)通過(guò)世界上最大的網(wǎng)絡(luò)-- -互聯(lián)網(wǎng)連接到數(shù)百萬(wàn)臺(tái)計(jì)算機(jī)上。

            網(wǎng)路模型

            網(wǎng)絡(luò)互聯(lián)模型有三種基本類(lèi)型:服務(wù)器端、客戶(hù)端以及點(diǎn)對(duì)點(diǎn)。

            使用服務(wù)器端模型,可以建立一個(gè)中央網(wǎng)絡(luò)互聯(lián)系統(tǒng)。其他計(jì)算機(jī)使用客戶(hù)端模型連接到服務(wù)器端后,就可以向服務(wù)器端發(fā)送數(shù)據(jù)以及從服務(wù)器端接收數(shù)據(jù)。客戶(hù)端沒(méi)有其他客戶(hù)端的信息,不直接與它們連接,客戶(hù)端都只知道服務(wù)器端的信息,而服務(wù)器端則擁有所有客戶(hù)端的信息以及適合這些客戶(hù)端之間的路由信息。服務(wù)器端和客戶(hù)端常常成對(duì)進(jìn)行描述,即服務(wù)器端/客戶(hù)端(C/S)模型,但是在使用DirectPlay時(shí),將二者分開(kāi)是有必要的,因?yàn)榉?wù)器端和客戶(hù)端是由兩個(gè)獨(dú)立的組件組成的。

            下圖演示了服務(wù)器端和客戶(hù)端模型之間的關(guān)系:



            點(diǎn)對(duì)點(diǎn)(peer-to-peer)這種網(wǎng)絡(luò)互聯(lián)模型與服務(wù)器端模型或客戶(hù)端模型正好相反,計(jì)算機(jī)相互之間直接進(jìn)行連接。每一臺(tái)新的計(jì)算機(jī)加入網(wǎng)絡(luò)會(huì)話中,都會(huì)建立一個(gè)新的連接,所以每臺(tái)計(jì)算機(jī)都能直接連接到其他計(jì)算機(jī)。連接到網(wǎng)絡(luò)的時(shí)段稱(chēng)為會(huì)話,一次會(huì)話會(huì)有與之相關(guān)的屬性,如密碼、最大連接數(shù)等。

            如下圖所示,一個(gè)4臺(tái)計(jì)算機(jī)的網(wǎng)絡(luò)擁有12個(gè)連接(每臺(tái)計(jì)算機(jī)與其他三臺(tái)計(jì)算機(jī)之間都有一個(gè)連接)。



            游戲廳

            可以將游戲廳服務(wù)器看作在線玩家的會(huì)議大廳,一個(gè)游戲廳允許所有玩家登錄、通信以及加入他們喜愛(ài)的一些游戲。一旦游戲廳服務(wù)器連滿了玩家,就停止對(duì)游戲廳的循環(huán)(這樣做是為了節(jié)省網(wǎng)絡(luò)帶寬)。網(wǎng)絡(luò)帶寬(network bandwidth)指的是一個(gè)網(wǎng)絡(luò)連接能夠輕松處理的數(shù)據(jù)量,高網(wǎng)絡(luò)帶寬連接能比低網(wǎng)路帶寬連接更快地處理大量的網(wǎng)絡(luò)數(shù)據(jù)。

            響應(yīng)時(shí)間和延遲

            帶寬引出了兩個(gè)術(shù)語(yǔ):響應(yīng)時(shí)間和延遲。響應(yīng)時(shí)間是完成一個(gè)操作所花的時(shí)間(越低越好)的量化。延遲是用來(lái)描述網(wǎng)絡(luò)通信的遲滯的術(shù)語(yǔ),即數(shù)據(jù)從發(fā)送到它被接收到所花的時(shí)間。

            低延遲表示網(wǎng)絡(luò)數(shù)據(jù)迅速地被接收。高延遲(最不希望出現(xiàn)的事情)表示網(wǎng)絡(luò)數(shù)據(jù)被延遲或者根本沒(méi)有被發(fā)送到。延遲是一個(gè)主要問(wèn)題,特別是使用了互聯(lián)網(wǎng)時(shí),就必須處理這個(gè)問(wèn)題。

            通信協(xié)議

            網(wǎng)絡(luò)可以有各種方式進(jìn)行相互通信,但是要連接到另一個(gè),兩個(gè)系統(tǒng)都必須采用相同的協(xié)議。目前最流行的協(xié)議是TCP/IP協(xié)議(傳輸控制協(xié)議 /Internet協(xié)議),它被廣泛應(yīng)用于互聯(lián)網(wǎng)。通信協(xié)議也被稱(chēng)作服務(wù)器提供者,無(wú)論服務(wù)器提供者是一種諸如IPX、TCP/IP的協(xié)議,還是一種諸如調(diào)制解調(diào)器或者串行電纜的設(shè)備,都可以把它看作是網(wǎng)絡(luò)互聯(lián)的連接體。

            TCP/IP協(xié)議是一種在網(wǎng)絡(luò)上傳輸數(shù)據(jù)包的方法。它將數(shù)據(jù)分割成很多小數(shù)據(jù)包,再加上發(fā)送方和接收方的地址以及包的數(shù)目,便于重組數(shù)據(jù)。

            如下圖所示:

            信息在傳輸過(guò)程中丟失(頻繁發(fā)生的事情),TCP/IP允許網(wǎng)絡(luò)重發(fā)數(shù)據(jù)包。當(dāng)出現(xiàn)延遲時(shí),這些數(shù)據(jù)包可能以錯(cuò)誤的順序被接收,比如舊的數(shù)據(jù)包在新的數(shù)據(jù)包之后到達(dá)。好在無(wú)需擔(dān)心,因?yàn)門(mén)CP/IP會(huì)重發(fā)丟失的包并且重組無(wú)序的包。

            尋址

            在TCP/IP協(xié)議下,一個(gè)系統(tǒng)被分配一個(gè)由4個(gè)數(shù)字(0 -255之間)組成的網(wǎng)絡(luò)地址(IP地址),數(shù)字之間用點(diǎn)分割。一個(gè)IP地址就像下面這樣:

            64.120.53.2

            IP地址對(duì)我們來(lái)說(shuō)不好辨認(rèn),但是網(wǎng)絡(luò)卻可以根據(jù)每一個(gè)數(shù)值成功地傳遞數(shù)據(jù)。稍加運(yùn)算就會(huì)發(fā)現(xiàn),這4個(gè)數(shù)字的組合總共可以提供4294967296個(gè)可能的地址。為了增加地址數(shù)量,網(wǎng)絡(luò)使用附加的稱(chēng)為端口的地址值,數(shù)據(jù)被傳送到其上。

            如果將IP地址比喻成一個(gè)郵政室(mailroom),那么該郵政室(IP地址)描述了與網(wǎng)絡(luò)相連的單個(gè)計(jì)算機(jī)系統(tǒng),并且該計(jì)算機(jī)系統(tǒng)只被分配了惟一的IP 地址。在郵政室中有很多箱柜(端口),郵件被分類(lèi)到其中。每一個(gè)箱柜(端口)屬于一個(gè)特定的工作人員(一個(gè)特定的應(yīng)用程序)。一些應(yīng)用程序可以擁有多個(gè)端口,數(shù)據(jù)只能被一個(gè)同相應(yīng)IP地址以及相應(yīng)端口所匹配的系統(tǒng)接收。一種稱(chēng)為數(shù)據(jù)路由器(data router)的設(shè)備將接收到的網(wǎng)絡(luò)數(shù)據(jù)傳送到它所知道的系統(tǒng),或者將數(shù)據(jù)傳送到另一個(gè)網(wǎng)絡(luò)連接。

            下圖演示了路由的過(guò)程,說(shuō)明網(wǎng)絡(luò)數(shù)據(jù)可以通過(guò)數(shù)據(jù)路由器進(jìn)行傳遞。



            DirectPlay概述

            要在工程中使用DirectPlay,必須包含DPlay8.h和DPAddr.h,還要鏈接DPlayX.lib和 DXGuid.lib。

            網(wǎng)絡(luò)對(duì)象

            使用DirectPlay,需要使用前面提到的網(wǎng)絡(luò)模型:客戶(hù)端、服務(wù)器端和點(diǎn)對(duì)點(diǎn)。每一種網(wǎng)絡(luò)模型都有自己的接口對(duì)象,如下所示:

            IDirectPlay8Client:客戶(hù)端網(wǎng)絡(luò)對(duì)象,連接到一個(gè)服務(wù)器端。
            IDirectPlay8Server:服務(wù)器端網(wǎng)絡(luò)對(duì)象,連接多個(gè)客戶(hù)端。
            IDirectPlay8Peer:   點(diǎn)對(duì)點(diǎn)網(wǎng)絡(luò)對(duì)象,連接客戶(hù)端其他客戶(hù)端。
            IDirectPlay8Address:包含(以及構(gòu)造)網(wǎng)絡(luò)地址的對(duì)象。

            來(lái)看看IDirectPlay8Client的簡(jiǎn)要說(shuō)明:

            Applications use the methods of the IDirectPlay8Client interface to create and manage client applications for client/server sessions.

            The methods of the IDirectPlay8Client interface can be organized into the following groups.

            Session Management Close
              Connect
              EnumHosts
              EnumServiceProviders
              GetApplicationDesc
              GetCaps
              GetSPCaps
              SetCaps
              SetSPCaps
            Message Management GetSendQueueInfo
              Initialize
              ReturnBuffer
              Send
            Client Information SetClientInfo
            Server Information GetServerInfo
            Miscellaneous CancelAsyncOperation
              RegisterLobby
              GetConnectionInfo
              GetServerAddress

            來(lái)看看IDirectPlay8Server的簡(jiǎn)要說(shuō)明:

            Applications use the methods of the IDirectPlay8Server interface to create and manage the server for a Microsoft® DirectPlay® client/server transport session

            The methods of the IDirectPlay8Server interface can be organized into the following groups.

            Session Management Close
              EnumServiceProviders
              GetApplicationDesc
              GetCaps
              GetSPCaps
              GetSendQueueInfo
              Host
              Initialize
              ReturnBuffer
              SendTo
              SetApplicationDesc
              SetCaps
              SetServerInfo
              SetSPCaps
            Client Management DestroyClient
              GetClientInfo
              GetPlayerContext
            Group Management AddPlayerToGroup
              CreateGroup
              DestroyGroup
              EnumPlayersAndGroups
              EnumGroupMembers
              GetGroupContext
              GetGroupInfo
              RemovePlayerFromGroup
              SetGroupInfo
            Miscellaneous CancelAsyncOperation
              GetClientAddress
              GetConnectionInfo
              GetLocalHostAddresses
              RegisterLobby


            要連接到遠(yuǎn)程網(wǎng)絡(luò)系統(tǒng)(或主持一次會(huì)話),需要使用 IDirectPlay8Address構(gòu)造一個(gè)網(wǎng)絡(luò)地址,IDirectPlay8Address的惟一用途就是構(gòu)造并包含單個(gè)網(wǎng)絡(luò)地址。會(huì)話指的是主持或加入一個(gè)可連接網(wǎng)路系統(tǒng)的時(shí)段,終止連接時(shí),會(huì)話也就終止了。要主持游戲會(huì)話或連接到遠(yuǎn)程系統(tǒng),首先需要?jiǎng)?chuàng)建網(wǎng)絡(luò)對(duì)象并給它分配地址。要主持游戲會(huì)話,只需要等待其他系統(tǒng)(也就是使用這些系統(tǒng)的人)連接上來(lái)即可。連接建立之后,系統(tǒng)就可以和遠(yuǎn)程系統(tǒng)開(kāi)始相互傳輸游戲相關(guān)的網(wǎng)絡(luò)消息, DirectPlay把這些遠(yuǎn)程系統(tǒng)稱(chēng)作玩家。

            將玩家進(jìn)行分組

            在DirectPlay 術(shù)語(yǔ)中,“玩家”是指通過(guò)網(wǎng)絡(luò)連接到其他計(jì)算機(jī)的單個(gè)連接(通常是一個(gè)游戲玩家)。一臺(tái)計(jì)算機(jī)可以有多個(gè)玩家,但一般只有一個(gè)。實(shí)際上,服務(wù)器端也被標(biāo)識(shí)為玩家,以便于識(shí)別。每一個(gè)玩家都會(huì)接收到一個(gè)標(biāo)識(shí)號(hào)碼(玩家ID),系統(tǒng)使用這個(gè)標(biāo)識(shí)號(hào)碼來(lái)將消息傳遞到每個(gè)玩家。這些號(hào)碼是追蹤玩家的惟一可信任方法,所以需要讓程序?qū)λ鼈冞M(jìn)行相應(yīng)地處理。

            對(duì)于大型游戲,可能有成千上萬(wàn)已連接的玩家。為了在游戲中更好地處理這些玩家,可以將一些或所有玩家編制到一些組中。采用組的概念可以減少編程的繁瑣,主要原因在于可以將一個(gè)游戲區(qū)域(比如一張地圖或某個(gè)級(jí)別)中的多個(gè)玩家劃分為一組,并且一次向整個(gè)組發(fā)送網(wǎng)絡(luò)數(shù)據(jù),而不是單獨(dú)發(fā)送給各個(gè)玩家。使用組還有很多其他原因,但這個(gè)原因是最主要的。一個(gè)組中包含多少玩家以及創(chuàng)建多少個(gè)組完全沒(méi)有限制,組也可以屬于其他組。

            如下圖所示:



            帶消息的網(wǎng)絡(luò)互聯(lián)

            消息是分了類(lèi)的數(shù)據(jù)包,其中包含簡(jiǎn)單的結(jié)構(gòu)。每一個(gè)消息都有特定的含義,且都有一個(gè)與之對(duì)應(yīng)的宏,而且還和所使用的網(wǎng)絡(luò)模型有關(guān)。要接收消息,網(wǎng)絡(luò)對(duì)象必須給自己指定一個(gè)回調(diào)函數(shù),以便于每次消息到達(dá)時(shí)進(jìn)行調(diào)用。為了確保得到平滑的數(shù)據(jù)流,該函數(shù)根據(jù)數(shù)據(jù)類(lèi)型進(jìn)行分析并盡快地返回。發(fā)送消息,需要使用各個(gè)網(wǎng)絡(luò)對(duì)象的發(fā)送函數(shù)。這些函數(shù)易于使用,并且提供了許多發(fā)送選項(xiàng),包括保證發(fā)送(guaranteed delivery)、安全編碼(secure encryption)以及同步或異步發(fā)送。

            同步與異步

            DirectPlay提供的第一個(gè)發(fā)送選項(xiàng)是同步或異步發(fā)送消息的功能,也就是說(shuō)系統(tǒng)在發(fā)出發(fā)送數(shù)據(jù)指定之后交回控制權(quán)(異步),還是直到所有的數(shù)據(jù)都成功發(fā)送之后再交回控制權(quán)(同步)。很多時(shí)候使用異步方法,因?yàn)樗粫?huì)像同步方法那樣阻塞系統(tǒng)。

            安全性

            要引起注意的是任何時(shí)候都可能有人在截取并記錄游戲的網(wǎng)絡(luò)數(shù)據(jù)。因此,就需要采用一種安全的方式編碼消息數(shù)據(jù),使得那些解碼的黑客非常難讀懂先前的信息。使用安全網(wǎng)絡(luò)發(fā)送的不好之處在于它會(huì)稍稍減慢系統(tǒng)速度,因?yàn)楸仨氃跀?shù)據(jù)發(fā)送之前先編碼信息,然后在接收到之后進(jìn)行解碼。

            保證發(fā)送

            就像一些快遞公司保證送到包裹一樣, DirectPlay也能保證送到消息。可以將一組信息標(biāo)識(shí)為保證的,其余確定的就是DirectPlay將一直執(zhí)行發(fā)送操作直到發(fā)送成功,以保證將其發(fā)送到目標(biāo)地址(除非斷線),使用保證發(fā)送是通過(guò)在調(diào)用函數(shù)時(shí)指定一個(gè)惟一的標(biāo)志來(lái)實(shí)現(xiàn)的。保證發(fā)送的不好之處在于速度,保證發(fā)送在實(shí)際的游戲狀況下非常慢,游戲采用UDP(用戶(hù)數(shù)據(jù)報(bào)協(xié)議)發(fā)送方法,就不用關(guān)心數(shù)據(jù)是否被接收到(和TCP發(fā)送方法相反,它保證數(shù)據(jù)的發(fā)送)。

            節(jié)流

            有時(shí)系統(tǒng)會(huì)因?yàn)樵噲D處理流數(shù)據(jù)而過(guò)載,盡管這樣, DirectPlay有一個(gè)內(nèi)置的消息節(jié)流器,它丟棄了發(fā)送隊(duì)列中低優(yōu)先級(jí)的信息。

            下圖有助于更形象地理解節(jié)流機(jī)制的概念,想象一隊(duì)人在鎮(zhèn)上熱鬧的夜總會(huì)前等待,如果每個(gè)顧客表示一條消息,那么當(dāng)非常忙的時(shí)候,保鏢(節(jié)流機(jī)制)就必須拒絕那些隊(duì)列中較不重要的顧客。



            使用GUID識(shí)別應(yīng)用程序

            如何從眾多的網(wǎng)絡(luò)應(yīng)用程序中區(qū)分出自己的網(wǎng)絡(luò)應(yīng)用程序呢?解決這個(gè)問(wèn)題的方法就是給自己的應(yīng)用程序指定一個(gè)惟一的號(hào)碼,并且只允許使用相同號(hào)碼的應(yīng)用程序相互進(jìn)行連接。這個(gè)特殊的號(hào)碼就是 Windows程序員熟悉的GUID(全局惟一標(biāo)識(shí)符)。創(chuàng)建應(yīng)用程序之前,花一點(diǎn)時(shí)間給它設(shè)置一個(gè)惟一的GUID,并且保證所有通過(guò)網(wǎng)絡(luò)進(jìn)行連接的該應(yīng)用程序使用相同的GUID。

            初始化網(wǎng)絡(luò)對(duì)象

            無(wú)論是服務(wù)器端、客戶(hù)端還是單點(diǎn)對(duì)象,使用DirectPlay的第一步都是創(chuàng)建網(wǎng)絡(luò)對(duì)象。要初始化每一個(gè)網(wǎng)絡(luò)模型接口,必須使用CoCreateInstance函數(shù),可能用到的類(lèi)ID和引用標(biāo)識(shí)符如下所示:

            CLSID_DirectPlay8Address  IID_IDirectPlay8Address
            CLSID_DirectPlay8Client IID_IDirectPlay8Client
            CLSID_DirectPlay8Peer IID_IDirectPlay8Peer
            CLSID_DirectPlay8Server IID_IDirectPlay8Server

            無(wú)論創(chuàng)建的是什么網(wǎng)絡(luò)對(duì)象(客戶(hù)端、單點(diǎn)或服務(wù)器端),都需要?jiǎng)?chuàng)建與之匹配的網(wǎng)絡(luò)回調(diào)函數(shù),在網(wǎng)絡(luò)消息被接收到的時(shí)候會(huì)調(diào)用該回調(diào)函數(shù)。

            該回調(diào)函數(shù)使用說(shuō)明如下:

            PFNDPNMESSAGEHANDLER is an application-defined callback function used by the IDirectPlay8Peer, IDirectPlay8Client, and IDirectPlay8Server IDirectPlay8LobbyClient and IDirectPlay8LobbiedApplication interfaces to process messages.

            typedef HRESULT (WINAPI *PFNDPNMESSAGEHANDLER)( 
            PVOID pvUserContext,
            DWORD dwMessageType,
            PVOID pMessage
            );

            Parameters

            pvUserContext
            Pointer to the application-defined structure that will be passed to this callback function. This is defined in the pvUserContext parameter of the Initialize method.
            dwMessageType
            One of the following message types that are generated by the IDirectPlay8Peer, IDirectPlay8Client, and IDirectPlay8Server interfaces. Each interface uses a different subset of the available messages. Refer to the interface documentation for details.
            • DPN_MSGID_ADD_PLAYER_TO_GROUP
            • DPN_MSGID_ASYNC_OP_COMPLETE
            • DPN_MSGID_CLIENT_INFO
            • DPN_MSGID_CONNECT_COMPLETE
            • DPN_MSGID_CREATE_GROUP
            • DPN_MSGID_CREATE_PLAYER
            • DPN_MSGID_DESTROY_GROUP
            • DPN_MSGID_DESTROY_PLAYER
            • DPN_MSGID_ENUM_HOSTS_QUERY
            • DPN_MSGID_ENUM_HOSTS_RESPONSE
            • DPN_MSGID_GROUP_INFO
            • DPN_MSGID_HOST_MIGRATE
            • DPN_MSGID_INDICATE_CONNECT
            • DPN_MSGID_INDICATED_CONNECT_ABORTED
            • DPN_MSGID_PEER_INFO
            • DPN_MSGID_RECEIVE
            • DPN_MSGID_REMOVE_PLAYER_FROM_GROUP
            • DPN_MSGID_RETURN_BUFFER
            • DPN_MSGID_SEND_COMPLETE
            • DPN_MSGID_SERVER_INFO
            • DPN_MSGID_TERMINATE_SESSION

              Additionally, if the application supports Microsoft® DirectPlay® lobby functionality, this parameter can specify one of the following message types that are generated by the IDirectPlay8LobbyClient and IDirectPlay8LobbiedApplication interfaces. Each interface uses a different subset of the available messages. Refer to the interface documentation for details.

            • DPL_MSGID_CONNECT
            • DPL_MSGID_CONNECTION_SETTINGS
            • DPL_MSGID_DISCONNECT
            • DPL_MSGID_RECEIVE
            • DPL_MSGID_SESSION_STATUS
            pMessage
            Structure containing message information.

            Return Values

            See the documentation for the individual messages for appropriate return values. Unless otherwise noted, this function should return S_OK.

            Remarks

            This function must be threadsafe because it might be called reentrantly through multiple threads.

            Callback messages from the same player are serialized. Once you receive a message from a player, you will not receive another until you have handled the first message, and the callback function has returned.

            The message structures have the same name as the message type except the "DPN_MSGID" is replaces with "DPNMSG". For example, the DPN_MSGID_CONNECTION_TERMINATED message type uses the DPNMSG_CONNECTION_TERMINATED message structure to convey the actual message information.

            When implementing this callback function, first look at the message type returned in the dwMessageType parameter and then cast the message structure (pMessage) to that type to obtain message information. Some messages don't have a defined structure because they have no parameters. For these messages, the pMessage parameter is NULL.


            要初始化網(wǎng)絡(luò)對(duì)象,需要調(diào)用Initialize函數(shù)。

            Registers an entry point in the client's code that receives the messages from the IDirectPlay8Client interface and from the server. This method must be called before calling any other methods of this interface.

            HRESULT Initialize(
            PVOID const
            pvUserContext,
            const PFNDPNMESSAGEHANDLER pfn,
            const DWORD dwFlags
            );

            Parameters

            pvUserContext
            [in] Pointer to the user-provided context value in calls to the message handler. Providing a user-context value can be useful to differentiate messages coming from multiple interfaces to a common message handler.
            pfn
            [in] Pointer to a PFNDPNMESSAGEHANDLER callback function that receives all messages from the server, and receives indications of session changes from the IDirectPlay8Client interface.
            dwFlags
            [in] You may specify the following flag.
            DPNINITIALIZE_DISABLEPARAMVAL
            Disable parameter validation for the current object.

            Return Values

            Returns S_OK if successful, or one of the following error values.

            DPNERR_INVALIDFLAGS
            DPNERR_INVALIDPARAM
             

            Remarks

            This is the first method you should call after using CoCreateInstance to obtain the IDirectPlay8Client interface.


            下面這個(gè)函數(shù)創(chuàng)建了DirectPlay Client對(duì)象并且初始化該對(duì)象。
             
            //--------------------------------------------------------------------------------
            // Create DirectPlay Client and initialize it.
            //--------------------------------------------------------------------------------
            BOOL Init_DirectPlay_Client()
            {
                
            // create DirectPlay client component
                if(FAILED(CoCreateInstance(CLSID_DirectPlay8Client, NULL, CLSCTX_INPROC, IID_IDirectPlay8Client, 
                                           (
            void**)&g_dp_client)))
                    
            return FALSE;

                
            // Assign a message handler to network component
                //
                // Registers an entry point in the client's code that receives the messages from the IDirectPlay8Client
                // interface and from the server.
                if(FAILED(g_dp_client->Initialize(NULL, Net_Msg_Handle, 0)))
                    
            return FALSE;

                
            return TRUE;
            }

            指定設(shè)備

            雖然已經(jīng)選擇了一個(gè)服務(wù)提供者,但系統(tǒng)中可能有一個(gè)以上的設(shè)備使用它。當(dāng)一個(gè)網(wǎng)絡(luò)適配器和調(diào)制解調(diào)器都連接到互聯(lián)網(wǎng),并且都使用TCP/IP協(xié)議時(shí),就會(huì)出現(xiàn)這種情況,這是就必須對(duì)設(shè)備進(jìn)行枚舉并進(jìn)行選擇。

            枚舉函數(shù)獲取了所有可用的服務(wù)提供者,并將它們以可用的方式組織在一起。DirectPlay枚舉和典型的 Windwos枚舉方法有所不同,在查詢(xún)過(guò)程中找到實(shí)例對(duì)象并不是每次都調(diào)用回調(diào)函數(shù),而是從包含了以數(shù)組形式存儲(chǔ)的每個(gè)服務(wù)提供者的緩沖區(qū)中尋找,如下圖所示:



            處理枚舉可以使用EnumServiceProviders函數(shù)。

            Enumerates the registered service providers available to the application.

            HRESULT EnumServiceProviders(
            const GUID *const
            pguidServiceProvider,
            const GUID *const pguidApplication,
            DPN_SERVICE_PROVIDER_INFO *const pSPInfoBuffer,
            PDWORD const pcbEnumData,
            PDWORD const pcReturned,
            const DWORD dwFlags
            );

            Parameters

            pguidServiceProvider
            [in] Pointer to a variable of type GUID that specifies a service provider. This optional parameter forces the enumeration of subdevices for the specified service provider. You should normally set this value to NULL, to enumerate all available service providers.
            pguidApplication
            [in] Pointer to a variable of type GUID that specifies an application. If a pointer is passed in this parameter, only service providers who can be connected to the application are enumerated. You can also pass NULL to enumerate the registered service providers for the system.
            pSPInfoBuffer
            [out] Pointer to an array of DPN_SERVICE_PROVIDER_INFO structures that will be filled with service provider information.
            pcbEnumData
            [out] Pointer to DWORD, which is filled with the size of the pSPInfoBuffer array, in bytes, if the buffer is too small.
            pcReturned
            [out] Pointer to a variable of type DWORD that specifies the number of DPN_SERVICE_PROVIDER_INFO structures returned in the pSPInfoBuffer array.
            dwFlags
            [in] The following flag can be specified.
            DPNENUMSERVICEPROVIDERS_ALL
            Enumerates all the registered service providers for the system, including those that are not available to the application or do not have devices installed.

            Return Values

            Returns S_OK if successful, or one of the following error values.

            DPNERR_BUFFERTOOSMALL
            DPNERR_INVALIDPARAM
             

            Remarks

            Call this method initially by specifying NULL in the pguidServiceProvider parameter to determine the base service providers available to the system. Specific devices for a service provider can be obtained by passing a pointer to a service provider GUID in the pguidServiceProvider. This is useful, for example, when using the Modem Connection for Microsoft® DirectPlay® service provider. You can choose among different modems for dialing out and select specific modems for hosting.

            If the pEnumData buffer is not big enough to hold the requested service provider information, the method returns DPNERR_BUFFERTOOSMALL and the cbEnumData parameter contains the required buffer size. Typically, the best strategy is to call the method once with a zero-length buffer to determine the required size. Then call the method again with the appropriate-sized buffer.

            Normally, this method will return only those service providers that can be used by the application. For example, if the IPX networking protocol is not installed, DirectPlay will not return the IPX service provider. To have DirectPlay return all service providers, even those that cannot be used by the application, set the DPNENUMSERVICEPROVIDERS_ALL flag in dwFlags.


            pSPInfoBuffer是指向DPN_SERVICE_PROVIDER_INFO結(jié)構(gòu)體數(shù)組的指針,該函數(shù)將用枚舉的信息填充該結(jié)構(gòu)體。

            Used when enumerating information for a specific service provider.

            typedef struct _DPN_SERVICE_PROVIDER_INFO{
            DWORD dwFlags;
            GUID guid;
            WCHAR* pwszName;
            PVOID pvReserved;
            DWORD dwReserved;
            } DPN_SERVICE_PROVIDER_INFO, *PDPN_SERVICE_PROVIDER_INFO;

            Members

            dwFlags
            Reserved. Must be 0.
            guid
            GUID for the service provider.
            pwszName
            Name of the service provider.
            pvReserved
            Reserved. Must be 0.
            dwReserved
            Reserved. Must be 0.

            下面這個(gè)函數(shù)演示了如何枚舉系統(tǒng)中的服務(wù)提供者:
             
            HWND g_hwnd;    // window handles

            IDirectPlay8Client* g_dp_client;    
            // directplay client

            // service provider list, used when enumerating information for a specifice service provider.
            DPN_SERVICE_PROVIDER_INFO*  g_sp_list;  

            DWORD g_sp_number;      
            // service provider number

            //--------------------------------------------------------------------------------
            // Enumerate all available service provider.
            //--------------------------------------------------------------------------------
            void Enum_Service_Provider()
            {
                
            // return is no server object
                if(g_dp_client == NULL)
                    
            return;

                
            // get a handler to the list box
                HWND listbox = GetDlgItem(g_hwnd, IDC_SERVICE_PROVIDERS);

                
            // clear the list box
                SendMessage(listbox, LB_RESETCONTENT, 0, 0);

                
            // release service provider list memory
                delete[] g_sp_list;
                g_sp_list = NULL;
                
                
            // query the required size of the data buffer
                
                DWORD sp_list_size = 0;

                
            // enumerates the registerd service providers available to the application
                HRESULT rv = g_dp_client->EnumServiceProviders(NULL, NULL, g_sp_list, &sp_list_size, &g_sp_number, 0);

                
            if(rv != DPNERR_BUFFERTOOSMALL)
                    
            return;

                
            // allocate a buffer
                if((g_sp_list = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[sp_list_size]) == NULL)
                    
            return;

                
            // enumerate again
                if(SUCCEEDED(g_dp_client->EnumServiceProviders(NULL, NULL, g_sp_list, &sp_list_size, &g_sp_number, 0)))
                {
                    
            // enumeration is complete, scan through entries.

                    
            char sp_name[1024];

                    DPN_SERVICE_PROVIDER_INFO* sp_ptr = g_sp_list;
                    
                    
            for(DWORD i = 0; i < g_sp_number; i++)
                    {
                        
            // convert wide string into multi-byte string
                        wcstombs(sp_name, sp_ptr->pwszName, 1024);

                        
            // Add the service provider into box
                        //
                        // An application sends an LB_ADDSTRING message to add a string to a list box. 
                        // If the list box does not have the LBS_SORT style, the string is added to the end of the list. 
                        // Otherwise, the string is inserted into the list and the list is sorted.  
                        SendMessage(listbox, LB_ADDSTRING, 0, (LPARAM)sp_name);

                        
            // go to next service provider in buffer
                        sp_ptr++;
                    }
                }
            }

            下面給出一個(gè)完整實(shí)例來(lái)運(yùn)用剛才介紹的知識(shí),由于DirectX9 SDK已不包含DirectPlay,所以需要安裝DirectX8 SDK,并在Visual Studio中進(jìn)行相應(yīng)設(shè)置。

            點(diǎn)擊下載源碼和工程

             
            /***************************************************************************************
            PURPOSE:
                Enum Network Adapters Demo
             ***************************************************************************************/


            #include <windows.h>
            #include <dplay8.h>
            #include <dpaddr.h>
            #include "resource.h"

            #pragma comment(lib, "dxguid.lib")
            #pragma comment(lib, "dplayx.lib")

            #pragma warning(disable : 4996)

            #define Safe_Release(p) if((p)) (p)->Release();

            // window handles, class.
            HWND g_hwnd;
            char g_class_name[] = "EnumClass";

            IDirectPlay8Client* g_dp_client;    
            // directplay client

            // service provider list, used when enumerating information for a specifice service provider.
            DPN_SERVICE_PROVIDER_INFO*  g_sp_list;  

            DWORD g_sp_number;      
            // service provider number

            //----------------------------------------------------------------------------------------
            // callback function that receives all messages from the server, and receives indications 
            // of session changes from the IDirectPlay8Client interface. 
            //----------------------------------------------------------------------------------------
            HRESULT WINAPI Net_Msg_Handle(PVOID user_context, DWORD message_id, PVOID msg_buffer)
            {
                
            // return S_OK to signify the message was handled OK.
                return S_OK;
            }

            //--------------------------------------------------------------------------------
            // Create DirectPlay Client and initialize it.
            //--------------------------------------------------------------------------------
            BOOL Init_DirectPlay_Client()
            {
                
            // create DirectPlay client component
                if(FAILED(CoCreateInstance(CLSID_DirectPlay8Client, NULL, CLSCTX_INPROC, IID_IDirectPlay8Client, 
                                           (
            void**)&g_dp_client)))
                    
            return FALSE;

                
            // Assign a message handler to network component
                //
                // Registers an entry point in the client's code that receives the messages from the IDirectPlay8Client
                // interface and from the server.
                if(FAILED(g_dp_client->Initialize(NULL, Net_Msg_Handle, 0)))
                    
            return FALSE;

                
            return TRUE;
            }

            //--------------------------------------------------------------------------------
            // Enumerate all available service provider.
            //--------------------------------------------------------------------------------
            void Enum_Service_Provider()
            {
                
            // return is no server object
                if(g_dp_client == NULL)
                    
            return;

                
            // get a handler to the list box
                HWND listbox = GetDlgItem(g_hwnd, IDC_SERVICE_PROVIDERS);

                
            // clear the list box
                SendMessage(listbox, LB_RESETCONTENT, 0, 0);

                
            // release service provider list memory
                delete[] g_sp_list;
                g_sp_list = NULL;
                
                
            // query the required size of the data buffer
                
                DWORD sp_list_size = 0;

                
            // enumerates the registerd service providers available to the application
                HRESULT rv = g_dp_client->EnumServiceProviders(NULL, NULL, g_sp_list, &sp_list_size, &g_sp_number, 0);

                
            if(rv != DPNERR_BUFFERTOOSMALL)
                    
            return;

                
            // allocate a buffer
                if((g_sp_list = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[sp_list_size]) == NULL)
                    
            return;

                
            // enumerate again
                if(SUCCEEDED(g_dp_client->EnumServiceProviders(NULL, NULL, g_sp_list, &sp_list_size, &g_sp_number, 0)))
                {
                    
            // enumeration is complete, scan through entries.

                    
            char sp_name[1024];

                    DPN_SERVICE_PROVIDER_INFO* sp_ptr = g_sp_list;
                    
                    
            for(DWORD i = 0; i < g_sp_number; i++)
                    {
                        
            // convert wide string into multi-byte string
                        wcstombs(sp_name, sp_ptr->pwszName, 1024);

                        
            // Add the service provider into box
                        //
                        // An application sends an LB_ADDSTRING message to add a string to a list box. 
                        // If the list box does not have the LBS_SORT style, the string is added to the end of the list. 
                        // Otherwise, the string is inserted into the list and the list is sorted.  
                        SendMessage(listbox, LB_ADDSTRING, 0, (LPARAM)sp_name);

                        
            // go to next service provider in buffer
                        sp_ptr++;
                    }
                }
            }

            //--------------------------------------------------------------------------------
            // Enumerate all adapters which specified by sp_guid.
            //--------------------------------------------------------------------------------
            void Enum_Adapters(GUID* sp_guid)
            {
                
            // return if no server object or GUID
                if(g_dp_client == NULL || sp_guid == NULL)
                    
            return;

                
            // get a handle of the list box
                HWND listbox = GetDlgItem(g_hwnd, IDC_ADAPTERS);

                
            // clear the list box
                SendMessage(listbox, LB_RESETCONTENT, 0, 0);

                DPN_SERVICE_PROVIDER_INFO* adapter_list = NULL;
                DWORD adapter_number = 0;
                DWORD adapter_list_size = 0;

                
            // query the required size of the data buffer
                HRESULT rv = g_dp_client->EnumServiceProviders(sp_guid, NULL, adapter_list, &adapter_list_size, &adapter_number, 0);

                
            if(rv != DPNERR_BUFFERTOOSMALL)
                    
            return;

                
            // allocate a buffer
                if((adapter_list = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[adapter_list_size]) == NULL)
                    
            return;

                
            // enumerate again
                if(SUCCEEDED(g_dp_client->EnumServiceProviders(sp_guid, NULL, adapter_list, &adapter_list_size, &adapter_number, 0)))
                {
                    
            char adapter_name[1024];

                    
            // enumeration is complete, scan through entries.
                    DPN_SERVICE_PROVIDER_INFO* adapter_ptr = adapter_list;

                    
            for(DWORD i = 0; i < adapter_number; i++)
                    {
                        
            // convert wide string into multi-byte string
                        wcstombs(adapter_name, adapter_ptr->pwszName, 1024);

                        
            // add the adapter name int listbox
                        SendMessage(listbox, LB_ADDSTRING, 0, (LPARAM)adapter_name);

                        
            // go to next servicec provider
                        adapter_ptr++;
                    }
                }
                
                
            // delete the list memory resources
                delete[] adapter_list;
            }

            //--------------------------------------------------------------------------------
            // Release all resource which allocated for DirectPlay.
            //--------------------------------------------------------------------------------
            void Release_DirectPlay()
            {
                
            // release client service provider list memory
                delete[] g_sp_list;
                g_sp_list = NULL;
                
                g_sp_number = 0;

                
            // release client component
                if(g_dp_client != NULL)
                {
                    
            // Closes the open connection to a session. This method must be called on any object that is successfully 
                    // initialized with a call to the IDirectPlay8Client::Initialize method.
                    g_dp_client->Close(0);

                    g_dp_client->Release();

                    g_dp_client = NULL;
                }
            }

            //--------------------------------------------------------------------------------
            // Window procedure.
            //--------------------------------------------------------------------------------
            long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
            {
                
            switch(msg)
                {
                
            case WM_COMMAND:
                    
            // The WM_COMMAND message is sent when the user selects a command item from a menu, when a control sends a 
                    // notification message to its parent window, or when an accelerator keystroke is translated. 
                    //
                    // wParam:
                    //    The high-order word specifies the notification code if the message is from a control. 
                    //    If the message is from an accelerator, this value is 1. If the message is from a menu, this value is zero. 
                    //    The low-order word specifies the identifier of the menu item, control, or accelerator. 
                    //
                    // lParam:
                    //     Handle to the control sending the message if the message is from a control. 
                    //     Otherwise, this parameter is NULL. 

                    // An application sends the LBN_SELCHANGE notification message when the selection in a list box is about to 
                    // change. The parent window of the list box receives this notification message through the WM_COMMAND message. 
                    if(LOWORD(wParam) == IDC_SERVICE_PROVIDERS && HIWORD(wParam) == LBN_SELCHANGE)
                    {
                        
            // Get the selection from the list
                        //
                        // Send an LB_GETCURSEL message to retrieve the index of the currently selected item, 
                        // if any, in a single-selection list box.         
                        unsigned int selected = (unsigned int) SendMessage(GetDlgItem(hwnd, IDC_SERVICE_PROVIDERS), LB_GETCURSEL, 0, 0);

                        
            // enumerate the adapters
                        DPN_SERVICE_PROVIDER_INFO* sp_ptr = g_sp_list;
                        sp_ptr += selected;
                        
                        
            // enumerate adapter with specified guid
                        Enum_Adapters(&sp_ptr->guid);
                    }

                    
            return 0;

                
            case WM_DESTROY:
                    Release_DirectPlay();
                    PostQuitMessage(0);
                    
            return 0;
                }

                
            return (long) DefWindowProc(hwnd, msg, wParam, lParam);
            }

            //--------------------------------------------------------------------------------
            // Main function, routine entry.
            //--------------------------------------------------------------------------------
            int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
            {
                WNDCLASS            win_class;
                MSG                 msg;    

                
            // create window class and register it
                win_class.style         = CS_HREDRAW | CS_VREDRAW;
                win_class.lpfnWndProc   = Window_Proc;
                win_class.cbClsExtra    = 0;
                win_class.cbWndExtra    = DLGWINDOWEXTRA;
                win_class.hInstance     = inst;
                win_class.hIcon         = LoadIcon(inst, IDI_APPLICATION);
                win_class.hCursor       = LoadCursor(NULL, IDC_ARROW);
                win_class.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
                win_class.lpszMenuName  = NULL;
                win_class.lpszClassName = g_class_name;    

                
            if(! RegisterClass(&win_class))
                    
            return FALSE;

                
            // create the main window
                g_hwnd = CreateDialog(inst, MAKEINTRESOURCE(IDD_ENUM), 0, NULL);

                ShowWindow(g_hwnd, cmd_show);
                UpdateWindow(g_hwnd);

                
            // initialize COM
                //
                // initialize the COM library on the current thread and identifies the concurrency model as single-thread
                // apartment (STA).
                CoInitialize(0);

                
            // Initialzie DirectPlay and enumerate service providers.
                if(! Init_DirectPlay_Client())
                {
                    MessageBox(NULL, "Error initializing DirectPlay.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
                    
            return 0;
                }

                
            // enumerate all available service provider
                Enum_Service_Provider();

                
            // start message pump, waiting for signal to quit.
                ZeroMemory(&msg, sizeof(MSG));

                
            while(msg.message != WM_QUIT)
                {
                    
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                    {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);            
                    }             
                }

                UnregisterClass(g_class_name, inst);

                
            // release COM system
                //
                // Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other
                // resources that the thread maintains, and forces all RPC connections on the thread to close.
                CoUninitialize();
                
                
            return (int) msg.wParam;
            }
             

            運(yùn)行截圖:



            posted on 2007-08-06 19:50 lovedday 閱讀(2270) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            公告

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            隨筆分類(lèi)(178)

            3D游戲編程相關(guān)鏈接

            搜索

            最新評(píng)論

            久久精品99久久香蕉国产色戒 | …久久精品99久久香蕉国产| 久久被窝电影亚洲爽爽爽| 久久综合给合综合久久| 久久久久四虎国产精品| 色婷婷久久综合中文久久蜜桃av | 久久午夜综合久久| 亚洲人成无码www久久久| 久久精品国产影库免费看| 91精品无码久久久久久五月天| 日韩人妻无码精品久久免费一| 国产91久久精品一区二区| 久久99国产一区二区三区| 狠狠综合久久综合中文88| 久久人做人爽一区二区三区| 亚洲一级Av无码毛片久久精品| 人妻精品久久久久中文字幕一冢本| 久久精品国产精品国产精品污| 久久精品国产WWW456C0M| 久久精品国产一区二区电影| 亚洲人成网亚洲欧洲无码久久| 久久被窝电影亚洲爽爽爽| 合区精品久久久中文字幕一区| 日韩电影久久久被窝网| 久久发布国产伦子伦精品 | 国产精品久久国产精麻豆99网站| 久久久WWW成人免费精品| 久久ww精品w免费人成| 亚洲国产成人久久综合野外| 青草影院天堂男人久久| 伊人久久综合成人网| 欧美亚洲日本久久精品| 99久久综合狠狠综合久久止| 无码国内精品久久综合88 | 精品国产综合区久久久久久| 亚洲精品无码久久一线| 亚洲国产精品一区二区三区久久| 欧美激情精品久久久久| 丰满少妇人妻久久久久久| 久久香蕉国产线看观看乱码| 成人午夜精品无码区久久|