zoukankan      html  css  js  c++  java
  • [翻译]XNA 3.0 Game Programming Recipes之fiftysix


    PS:自己翻译的,转载请著明出处

                                                                                     8-4 在网络上发送/接收数据
    问题
                                     创建和加入一个网络会话是一回事,但是如果你没有发送和接收任何数据,网络又有什么好处呢?
    解决方案
                                     当玩家已经连接到了这个会话,你可以保存所有你想要发送到PacketWriter流里的数据。一旦这个已经被完成,你可以发送这个PacketWriter到所有的玩家在这个会话中,使用这个LoaclNetworkPlayer.SendData方法。
                                     在你为一个在本地机器上的玩家接收任何数据之前,你应该检查LocalNetworkGamer.IsDataAvailable的标志是否设置成true,它表示数据已经接收到了并且准备被处理。
                                     只要这个标志是true,你应该调用LocalNetworkGamer.ReceiveData方法,返回一个PacketReader包含其他玩家发送到本地玩家的所有的数据。
    它是如何工作的
                                     这节创建在前面章节的结果上,它允许多台机器在同一网络通过会话互连。这个程序结束在InSession状态,它简单调用会话的Update方法。
                                     这里,你将会使InSession状态其实做了些事情,所以你的玩家将发送一些数据到所有其他的玩家在这个会话中。作为一个例子,你发送程序已经运行的分钟数和秒数。
                                     接下来,你监听到你的玩家的任何数据变量。如果这里有数据变量,你将会接收两个数并且把他们翻译成字符串,然后打印在屏幕上。
                                     为了发送和接收数据,你需要一个PacketWriter对象和一个PacketReader对象,这样添加这些两个变量到你的代码中:
    1 PackWriter writer=new PacketWriter();
    2 PackReader reader=new PacketReader();

                                     这里没有理由有更多的一个PacketWriter对象和一个PacketReader对象在你的项目中。

    Sending Data to Other Players in the Session(在会话中发送数据到其他的玩家)
                                     你应该首先保存所有在你想要发送给其他玩家的数据在PacketWriter中.你可以通过传递你的数据作为参数到它的Write方法中来实现它:

    1 writer.Write(gameTime.TotalGameTime.Minutes);
    2 writer.Write(gameTime.TotalGameTime.Seconds);
                                     在你保存所有的你的数据在PacketWriter中,你可以使用这个你本地玩家的SendData方法发送它到所有的其他的玩家:
    1 LocalNetworkGamer localGamer=networkSession.LocalGamer[0];
    2 localGamer.SendData(writer,SendDataOptions.None);

                                     SendDataOptions参数将会解释在本章节的末尾。相当重要,这个SendData方法有一个重载版本,它允许你发送数据到唯一一个指定玩家,而不是到所有连接在这个会话中的玩家。
                                     这是你所需要去做的,使你的数据发送到其他连接在这个会话中的玩家!

    Receiving Data from Other Players in the Session(在会话中接收其他玩家的数据)
                                     基本上,为了从其他玩家那里接收数据,你做这个反转:你调用你的本地玩家的ReceiveData方法,它将返回一个PacketReader包含所有通过其他玩家发送的数据。调用PacketReader.Read方法中的一个去从PacketReader中获得你的数据

    1 NetworkGamer sender;
    2 localGamer.ReceiveData(reader,out sender);
    3 string playerName=sender.Gamertag;
    4 int minutes=reader.ReadInt32();
    5 int seconds=reader.ReadInt32();

                                     这个ReceiveData方法将保存数据在PacketReader流里面。作为奖励,玩家它发送这个数据将会保存在第二个参数中。这样你知道数据来自于哪里。
                                     当你从你的PacketReader里读取数据时,你需要确保你用与保存它的同样的顺序去读它。同样,因为PacketReader简单的包含一个字节流,你需要去指明哪个对象你想从字节里去构造它。例如,一个整型要求的比一个矩阵的字节少,所以在某些时候,你需要告诉PacketReader哪种对象你想要被保存。
                                     你在这个例子上发送分钟和秒都是整型,所以你想要从字节流里重构两个整型。仔细看下PacketReader的Read方法的不同,注意它支持哪种对象。如果你发送一个Matrix,你想要从字节流里使用ReadMatrix方法去重构它。使用这个ReadSignle方法去重构float,这个ReadDouble方法去读一个double,并且ReadInt16方法去读一个short.由于一般int使用的是32bit(4字节),你使用这个在前面的代码中的ReadInt32()方法。

    LocalGamer.IsDataAvailable
                                     如果多个玩家正在发送给你数据,可能你有多个字节流通过你的代码正在等待被读。这同样会发生如果其他玩家正在调用SendData比正在调用ReceiveDatag更高频率。
                                     在些情况下,你可以询问localGamer.IsDataAvailable属性,因为这将一直保持为true,当这里有一个字节流当代本地玩家去读。
                                     只要数据对你的玩家是可用的,下面的代码将接收一个新的PacketReader和读玩家的GamerTag属性,它发送这个数据,接下来,玩家程序已经运行的分钟和秒数从PacketReader里被读出。

     1 while(localGamer.IsDataAvailable)
     2 {
     3      NetworkGamer sender;
     4      localGamer.ReceiveData(reader,out sender);
     5      string gamerTime="";
     6      gamerTime+=sender.Gamertag+":";
     7      gamerTime+=sender.ReadInt32()+"m";
     8      gamerTime+=sender.ReadInt32()+"s";
     9      gamerTimes[sender.Gamertag]=gamerTime;
    10 }
                                      为了让这个例子实际上可以做些事情,这个数据被分解成一个字符串,调用gamerTime,它被保存在一个Dictionary的内部。一个Dictionary是默认的类.Net查找别,通过添加这个变量到你的代码中你可以创建它:
    1 Dictionary<string,string> gamerTimes=new Dictionary<string,string>();
                                      前面的代码将为每一个玩家创建一个条目在Dictionary,这些玩家发送数据给你。每次新的数据从一个玩家那里被接收,玩家的条目在Dictionary里被更新。在Draw方法里,你可以打印这个在这个Dictinary里字符串到屏幕上。
                                      只要一个玩家离开会话,你想要从Dictionary移动到相应的条目中。一个非常棒的方式可以作到是通过捕获相应的GamerLeft事件:
    1 void GamerLeftEventHandler(object sender,GamerLeftEventArgs e)
    2 {
    3      log.Add(e.Gamer.Gamertag+"left the current session");
    4      gamerTimes.Remove(e.Gamer.Gamertag);
    5 }


    SendDataOptions
                                      在这个会话中,当你发送数据到其他的玩家,你盼望所有你的消息到达接收者用你发送它们的同样顺序。由于因特网的结构,你的信息可以以与你发送它们的不同的顺序到达。甚至更糟,你的某些数据也许根本就没有到!
                                      幸运的是,你可以指定两个重要的因素为你发送的每个包。在你可以决定他们之前,你应该知道他们是什么,他们的益处是什么,这是十分重要的,其缺点又是什么:
                  1.Order of arrival:数据包应该以他们发出的同样被接收吗?
                  2.Reliability:你发送的数据绝对至关重要,或者游戏生存的某些数据包丢失了?
                                      这两个问题的答案是是/否,给你四种可能的组合。LocalNetworkGamer.SendData接收一个SendDataOptions标志作为一个第二个参数,它允许你去指定他们四个中的一个组合(它同样接收一个第5个标志,SendDataOptions.Chat,它在本章后面在讨论):
                  1.SendDataOptions.None:指定你将要发送的数据包并不重要,接收顺序也没有太大关系。
                  2.SendDataOptions.InOrder:指定你将要发送的数据包用与发送的相同的顺序去接收,但是如果丢失一两个数据包也没有什么问题。
                  3.SendDataOptions.Reliable:指定相反的SendDatOptions.InOrder.使用这个如果你的数据非常重要,意思是所有你发送的数据已经被到达接收器。不管,不关你怎么发送它们,数据包以不同的接收顺序接受也没有多大关系。
                  4.SendDataOptions.ReliabeInOrder:表明所有的数据已经到达接收器,并且以发送的顺序接收的。
                                      好,这就容易了;我将要拾取最后一个!不幸的是,某些操作有它的缺点,这里解释一下:
                  1.SendDataOption.None:这里没有什么速度处罚;数据只希望简单发送。
                  2.SendDataOptions.InOrder:在你发送它们之前,所有的包被分配一个序列号码。接收器检查它们的号码,并且如果一个包A在新的包B后面被收到,包A被简单的丢弃。这很容易去检查并且不需要花费时间,但是你的某些数据可以通过它被丢掉,尽管它们已经成功的到达了目的地。
                  3.SendDataOptions.Reliable:在这种情况下,接收器检查去看看哪个包丢失了。当包C似乎从包流ABDE中丢失,接收器要求发送者去发送包C。在此期间,包D和E可以访问你的XNA代码。
                  4.SendDataOptions.ReliableInOrder:使用这个当你的数据要求它。当这个接收器检测到包C从流ABDE中丢失,它要求发送者去重新发送包C。这时,后面的包D和E不会被传知道包C已经成功被接收。这将导致严重的迟缓,因为所有的后续的包被停止并且保存在内存中,直到包C已经被重新发送并且被接收到。
                                     作为一般的规则,它是很安全的使用SendDataOptions.InOrder为大多数游戏数据,同时SendDataOption.ReliableInOrder应该被用来作为一个小的可能。

    SendDataOptions.Chat
                                     在你开始通过网络发送数据时,你需要注意一件事:人对人的聊天信息通过因特网发送没有被加密,因为它被法律禁止的。直到密码学出现,默认的,为所有你发送的数据使用localGamer.SendData方法,你应该使用SendDataOptions.Chat去表示XNA不一定加密这样的信息。如果你想要,这可以被用在联合其他的SendDataOptions,就象这样:

    1 localGamer.SendData(writer,SendDataOptions.Chat|SendDataOptions.Reliable);
                                     请注意,你可以发送加密和不加密的信息混合在一起。一件事要注意,如果你这样做了,安排好聊天数据将只对应聊天的数据,而不是加密的数据。例如,让我们看下这个情况,你首先发送一个聊天信息,其次是数据信心,聊天信息,和数据信息,正如8-1左边所示:

                                     在图8-1的右部分显示四个可能的方式,数据如何到达接收器。在a的情况下,这个包以发出时的准确顺序已经到达接收器。在b和c的情况下,包的顺序改变了。重要的事要注意,不过,在这两种情况下,第一个聊天包A在第二个聊天包C之前接收到,并且第一个数据包B在第二个数据包D之间接收到。
                                     由于两个数据和聊天包可以被发送在同样的祯期间,你需要确保你不能混合它们在接收器端。一个可能的方式去做这个是在你发送它们之前,添加一个小的导言到你的包中,表示它是否是数据或者聊天包。在下面代码中将显示出来,其中D代表一个数据包和C代表聊天包:
    1 writer.Write("D");
    2 writer.Write(gameTime.TotalGameTime.Minutes);
    3 writer.Write(gameTime.TotalGameTime.Seconds);
    4 LocalNetworkGamer localGamer=networkSession.LocalGamers[0];
    5 LocalGamer.SendData(writer,SendDataOptions.ReliableInOrder);
    6 writer.Write("C");
    7 writer.Write("This is a chat message from"+localGamer.Gamertag);
    8 localGamer.SendData(writer,SendDataOptions.Chat|SendDataOptions.ReliableInOrder);
                                    在接收的一端,简单的检查包是否是一个D或者C包,并且因此处理这个包:
     1 while(localGamer.IsDataAvialable)
     2 {
     3     NetworkGamer sender;
     4     localGamer.ReceiveData(reader,out sender);
     5     string messageType=reader.ReadString();
     6     if(messageType=="D")
     7     {
     8          string gamerTime="";
     9          gamerTime+=sender.Gamertag+":";
    10          gamerTime+=sender.ReadInt32()+"m";
    11          gamerTime+=sender.ReadInt32()+"s";
    12          gamerTimes[sender.Gamertag]=gamerTime;
    13     }
    14     else if(mssageType=="C")
    15     {
    16          lastChatMessage[sender.Gamertag]=reader.ReadString();
    17     }
    18 }


    Multiple Local Players
                                     如果多个玩家被连接到同一台机器上,你想要通过它们叠代发送和接收它们的数据。
                                     发送它们的数据到所有的玩家是非常简单的:

    1 //send data from all local players to all other player in session
    2 foreach(LocalNetworkGamer localGamer in networkSession.LocalGamers)
    3 {
    4      writer.Write(gameTime.TotalGameTime.Munutes);
    5      writer.Write(gameTime.TotalGameTime.Seconds);
    6      localGamer.SendData(writer,SendDataOptions.ReliableInOrder);
    7 }
                                     请记住,你可以使用一个SendData方法重载的版本去发送数据到指定的玩家。
                                     在同样的机器上,为你的所有的本地玩家接收数据,一点也不难。只要确保你循环你的代码直到所有你的本地玩家的IsDataAvailable标志被设置成false:
     1 foreach(LocalNetworkGamer localGamer in networkSession.LocalGamers)
     2 {
     3    while(localGamer.IsDataAvailable)
     4    {
     5         NetworkGamer sender;
     6         localGamer.ReceiveData(reader,out sender);
     7         string gamerTime=localGamer.Gamertag+"received from";
     8         gamerTime+=sender.Gamertag+":";
     9         gamerTime+=sender.ReadInt32()+"m";
    10         gamerTime+=sender.ReadInt32()+"s";
    11         gamerTimes[sender.Gamertag]=gamerTime;
    12    }
    13 }


    代码  
                             (略)
    源代码:http://shiba.hpe.cn/jiaoyanzu/WULI/soft/xna.aspx?classId=4
    (完)

  • 相关阅读:
    ubuntu上virsh+kvm安装虚拟机
    Less(31)GET-BLIND-IMPIDENCED MISMATCH-Having a WAF in front of web application
    Less(26a)GET
    Less(26)Trick with comments and space (过滤了注释和空格的注入)
    Less(30)Get-Blind Havaing with WAF
    Less(29)基于WAF的一个错误
    Less(25a)Trick with OR & AND Blind (过滤了or和and的盲注)
    Less(25)Trick with OR & AND (过滤了or和and)
    Less(24)Second Degree Injections *Real treat* -Store Injections (二次注入)
    Less(23)GET
  • 原文地址:https://www.cnblogs.com/315358525/p/1561330.html
Copyright © 2011-2022 走看看