ProudNet是研究游戏软件开发的游戏服务端 &网络引擎。
本章对客户端之间的直接通信,即P2P通信进行讲述。 一般P2P连接 1.客户端1与客户端2建立P2P连接。 2.连接成功后客户端1与客户端2之间互发信息。 但是ProudNet并不是如此操作,而是服务器管理客户端之间的连接。这样做的原因是,假如客户端1被黑客攻击的话,连接任何客户端收发信息发生的隐患。 如以下的方式: 1.服务器发送客户端1与客户端2连接的命令。 2.客户端1与客户端2连接到P2P及时响应。 3.客户端1与客户端2之间互发信息。 ProudNet 具有P2P组的概念,P2P组是由HostID识别的对象,P2P组可添加0个以上的客户端,也可添加到服务端。P2P组中客户端之间利用P2P进行通信。 P2P组可自行定义。如:一个聊天室,游戏玩家的房间或者MMO游戏中假领域中的玩家。 当然,一个客户端可进入多个P2P组,重叠也可以。 客户端C1,C2 要建立一个组,参照一下代码。 1. [C++] 2. HostID list[2]; 3. list[0] = C1; 4. list[1] = C2; 5. G =s->CreateP2PGroup(list, 2); // 第二个要素=数组的大小 6. 7. [C#] 8. G =s.CreateP2PGroup({C1,C2});
G中包含了P2P组的HostID,服务器即可使用G。 那么客户端如何知道G存在?调用关联函数如下。 1. [C++] 2. c->Set_OnP2PMemberJoin([...] 3. (HostIDmemberHostID, // [1] 4. HostIDgroupHostID, // [2] 5. int memberCount, // [3] 6. const ByteArray&customField) 7. { 8. G =groupHostID; // [4] 9. Peers.Add(memberHostID); 10. }); 11. 12. 13. [C#] 14. c.P2PMemberJoinHandler = 15. (memberHostID, // [1] 16. groupHostID, // [2] 17. memberCount, // [3] 18. customField)=>{ 19. G =groupHostID; // [4] 20. Peers.Add(memberHostID); 21. };
[1]: memberHostID表示指向的主机及本地与P2P的连接。 [2]: memberHostID代表与进入了哪个P2P组。 [3] : P2P组进入多少个主机。 [4]: 要添加代码的部分。需要对“自身在哪个P2P组和此通信的主机是谁”进行保存。 客户端收到P2P通信的事件后,客户端即可直接发送给对方P2P信息。 使用SendUserMessage 或者RMI即可。调用SendUserMessage 或者RMI 做为收信对象添加其它主机的HostID即可发送到P2P。假设添加到G,将队G中的所有主机同时发送数据。 1. [C++] 2. c->SendUsermessage(G,RmiContext::ReliableSend, data, length); 3. 4. 5. [C#] 6. c.SendUserMessage(G,RmiContext.ReliableSend, data);
ProudNet中对P2P组可添加3个以上的客户端。对于已经建立的P2P组,调用JoinP2PGroup 将更多的主机添加到P2P组。对于新添加的主机和之前建立的主机用OnP2PMemberJoin事件进行接收。 P2P组中要删除主机调用NetServer.LeaveP2PGroup即可,要消除P2P组调用DestroyP2PGroup即可。此时,客户端通过OnP2PMemberLeave了解“谁离开了”。 即使建立P2P组通知给客户端也要进行打孔。打孔需要几秒时间,在客户端接收信息的期间NetServer 处理延迟,并在后端运行打孔。 打孔成功后通知给客户端, 即OnChangeP2PRelayState事件。 1. [C++] 2. c->Set_OnChangeP2PRelayState([...] 3. (HostIDremoteHostID, ErrorType reason) // [1] 4. { 5. ... 6. }); 7. 8. 9. [C#] 10. c.ChangeP2PRelayStateHandler = 11. remoteHostID,reason) =>{ // [1] 12. ... 13. };
[1]:客户端打孔状态发生了何种改变,并且告知改变的状态。假设 reason=Ok代表打孔成功,其它值代表打孔消失,显示如何消失的状态。 要想利用RMI进行P2P通信,将proxy和stub 附加到所有的NetClient 中。如下所示: 1. [MyGame.pidl] 2. 3. globalMyGameP2P //[1] 4. { 5. Player_Move([in] Vector3 position); 6. } 7. 8. 9. [C++] 10. MyGameP2P:: ProxyP2PProxy; //[2] 11. MyGameP2P::StubFunctionalP2PStub; 12. 13. P2PStub.Player_Move_Function =[...]PARAM_MyGameP2P_Player_Move{ //[3] 14. ... 15. }; 16. 17. c->AttachProxy(&P2PProxy); //[4] 18. c->AttachStub(&P2PStub); 19. 20. P2PProxy.Player_Move(G,RmiContext::UnreliableSend, myPosition); // [5] 21. 22. 23. [C#] 24. MyGameP2P.ProxyP2PProxy; //[2] 25. MyGameP2P.Stub P2PStub; 26. 27. P2PStub.Player_Move = (sendFrom,rmiContext, position)=>{ //[3] 28. ... 29. }; 30. 31. c.AttachProxy(P2PProxy); //[4] 32. c.AttachStub(P2PStub); 33. 34. P2PProxy.Player_Move(G,RmiContext.UnreliableSend, myPosition); // [5]
[1]: 定义P2P通信用RMI. [2]: P2P RMI的 proxy与 stub的一个实例类. [3]: 定义P2P接收RMI的处理函数.使用者自行定义. [4]: 附加[2] 的实例到NetClient。 [5]: 对P2P 客户端远程调用. 总结: 1.创建P2P组直接通信的CreateP2PGroup。 2. 通过OnP2PMemberJoin了解客户端之间的P2P通信。 3. RMI或者SendUserMessage 发送P2P信息。 4. 要使用P2P RMI的话Proxy与Stub都要附加到NetClient。
|