网络——在网络上发送,接收数据

问题

创建并加入一个网络会话是一回事,但如果不能发送或接收任何数据那么网络会话有什么用呢?

解决方案

当玩家连接到会话时,你可以在一个PacketWriter流中存储所有想要发送的数据。完成这个操作后,你可以使用LocalNetworkPlayer.SendData方法将这个PacketWriter发送给会话中的所有玩家。

在玩家接收数据前,你应该检查他们的LocalNetworkGamer. IsDataAvailable是否被设为ture,这表示数据已经被接收并做好了处理的准备。

一旦IsDataAvailable为true,你就可以调用LocalNetworkGamer.ReceiveData方法,返回一个包含另一个玩家发送给本地玩家的所有数据的PacketReader。

工作原理

这个教程建立在前一个教程结果的基础上,前一个教程允许同一网络上的多个机器通过一个会话互联。程序结束于InSession状态,这个状态只是简单地调用会话的Update方法。

现在,你将在InSession状态中做点实际的操作,让你的玩家可以将一些数据发送到会话中的其他玩家那里。本例中,你将发送程序运行的分钟数和秒数。

然后,你监听可用的数据。如果有可用的数据,你会接收两个数字,将它们放在一个字符串中,显示在屏幕上。

要发送和接收数据,你需要一个PacketWriter对象和一个PacketReader对象,所以在代码中添加这两个变量:

PacketWriter writer = new PacketWriter(); 
PacketReader reader = new PacketReader(); 

在项目中使用超过一个的PacketWriter对象和PacketReader对象是毫无理由的。

将数据发送到会话中的另一个玩家

你需要在PacketWriter中存储所有要发送给其他玩家的数据,这可以通过将数据作为Write方法的参数做到:

writer.Write(gameTime.TotalGameTime.Minutes); 
writer.Write(gameTime.TotalGameTime.Seconds); 

在将所有数据存储到PacketWriter之后,你可以使用本地玩家的SendData方法将它发送到所有其他用户:

LocalNetworkGamer localGamer = networkSession.LocalGamers[0]; 
localGamer.SendData(writer, SendDataOptions.None);

SendDataOptions参数会在教程的最后解释。更重要的是,SendData有一个重载方法可以只将数据发送到指定玩家而不是会话中的所有玩家。

以上就是将数据发送到会话中的其他玩家需要的所用操作。

从会话中的另一个玩家处接收数据

从其他玩家接收数据大致就是反过程:调用本地玩家的ReceiveData方法,这会返回一个包含其他玩家发送数据的PacketReader。调用PacketReader. Read方法中的一个从PacketReader获取数据:

NetworkGamer sender;
localGamer.ReceiveData(reader, out sender);string playerName = sender.Gamertag; 
int minutes = reader.ReadInt32();
int seconds = reader.ReadInt32();

ReceiveData方法存储PacketReader流中的数据,发送给你数据的玩家会存储在第二个参数中,这样你就可以知道数据来自于谁。

当从PacketReader读取数据时,你需要确保以与发送相同的顺序进行读取。而且,因为PacketReader只包含字节流,你需要告知你想从字节构建哪个对象。例如,一个整数需要的字节比矩阵少,所以需要在某些时候告知你想恢复为哪种类型的对象。

本例中分钟数和秒数为整数,所以你想从字节流中重新构建两个整数。看一下PacketReader的不同Read方法,注意支持哪个对象。如果你想重构矩阵,则应该使用ReadMatrix方法,使用ReadSingle方法重构float,ReadDouble方法重构double,ReadInt16 重构short。

LocalGamer.IsDataAvailable

如果多个玩家向你发送数据,可能会有多个字节流需要被读取,这种情况也会发生在其他玩家调用SendData的频率大于你调用ReceiveData的频率时。

在这种情况下,你可以查询localGamer.IsDataAvailable属性,因为只要有一个字节流正在等待本地游戏,这个属性就会为true。

只要数据对你的玩家可用,下面的代码就会接收一个新PacketReader并读取发送数据的玩家的GamerTag属性。然后,玩家程序运行的分钟数和秒数就会从PacketReader中读取。

while (localGamer.IsDataAvailable)
...{NetworkGamer sender;localGamer.ReceiveData(reader, out sender);string gamerTime = "";gamerTime += sender.Gamertag + ": "; gamerTime += reader.ReadInt32() + "m "; gamerTime += reader.ReadInt32() + "s"; gamerTimes[sender.Gamertag] = gamerTime;
}

要让这个例子实际干点事情,数据被转换到一个叫做gamerTime的字符串中,它存储在一个Dictionary。Dictionary是默认的generic .NET查询表,可以使用以下代码创建:

Dictionary<string, string> gamerTimes = new Dictionary<string, string>();

前面的代码会在Dictionary中创建一个数据项,对应发送给你数据的玩家。当从一个玩家接收新数据时,Dictionary中的对应数据项会被更新,你可以在Draw方法中将Dictionary中的字符串显示在屏幕上。

当玩家离开会话时,你需要将它们对应的数据项从Dictionary移除,这可以在GamerLeft 事件中加以处理:

void GamerLeftEventHandler(object sender, GamerLeftEventArgs e) 
...{log.Add(e.Gamer.Gamertag + " left the current session"); gamerTimes.Remove(e.Gamer.Gamertag);
}
SendDataOptions

当你将数据发送到会话中的其他玩家时,你期望到达接受者的信息的顺序与发送的顺序是相同的,但是基于Internet的原理,你的信息可能会以不同于发送的顺序到达,甚至更糟,有些数据可能根本就传不到!

幸运的是,你可以为发送的数据包指定两个重要的系数,在使用前你需要知道它们是什么,好处是什么,更重要的是,它们的缺点是什么:

  • 到达的顺序(Order of arrival):数据包接收的顺序是否与发送的顺序相同?
  • 安全性(Reliability):你发送的这个数据是否是至关紧要的,如果数据包丢失游戏还能进行吗?

以上两个问题不是是就是否,可以提供四种可能性。LocalNetworkGamer.SendData可以将SendDataOptions作为第二个参数,这个参数可以让你指定四种情况中的一个:

  • SendDataOptions.None:发送的数据不是关键的,你接收数据的顺序也无关紧要。
  • SendDataOptions.InOrder:接收数据的顺序必须和发送它们的顺序相同,但一些数据包丢失无大碍。
  • SendDataOptions.Reliable:与SendDatOptions.InOrder相反,你的数据是关键的,你发送的所有数据必须到达接收者。但是,接收数据的顺序是否和发送的顺序相同无关紧要。
  • SendDataOptions.ReliableInOrder:所有的数据必须以和发送顺序相同的顺序到达接收者。

不是很难,我选择最后一个!有些选项有点缺点,解释如下:

  • SendDataOptions.None:没有速度损失,只能指望数据能够成功发送。
  • SendDataOptions.InOrder:在数据发送前,所有的数据包被分配了一个序号。接收者检查这个序号,如果数据包A在一个更加新的数据包B之后被接收,数据包A会被抛弃。这是个简单的检查方法,几乎不花时间,但即使有些数据成功到达了目的地可能也会被抛弃。
  • SendDataOptions.Reliable:接收者会检查丢失了哪个数据包。当数据包C从数据包流ABDE中丢失时,接收者会要求发送者重新发送数据包C,同时,数据包D和E在XNA代码中可以被访问。
  • SendDataOptions.ReliableInOrder:只有在你的数据需要时才使用这个选项。当接收者法线数据包C从流ABDE中丢失时,它会让发送者重新发送数据包C。这次,其后的数据包D和E不会被接收者传递到XNA中,直到数据包C也被成功的传递。这会引起延迟,因为所有后继的数据包会保存在内存中直至数据包C被重新发送并收到才会被传递到XNA程序中。

普遍的原则是,对大多数数据来说SendDataOptions.InOrder是安全的,尽可能不要使用 SendDataOption.ReliableInOrder。

SendDataOption.Chat

在你开始发送数据前,你需要记住一件事情:在Internet上发送的聊天信息(chat message)不可以被加密,法律上是禁止的。

因为默认情况下使用localGamer.SendData方法发送数据都会进行加密,你必须使用SendDataOptions.Chat表示XNA不要加密聊天信息。你也可以使用SendDataOption的组合,如下所示:

localGamer.SendData(write,SengDataOptions.Chat|SendDataOptions.Reliable); 

注意你可以发送加密和不加密混合的信息。如果你这样干,排序过的聊天信息只根据聊天数据排序而不是根据加密过的数据。例如,让我们看一下发送第一条信息时的情况,依次是数据信息、聊天信息、数据信息,如图8-1左图所示。

image

图8-1 顺序发送4个数据包(左图)和4种接收数据的方式

图8-1的右图显示了数据如何到达接收端的4中可能方式。在a情况中,数据到达的顺序与发送的顺序一样。在情况b和c中,数据包的顺序发生了改变,但是,这两种情况中第一个聊天数据包A在在第二个聊天数据包C之前被接收,第一个数据包B在第二个数据包D之前。

因为两个数据包和两个聊天数据包可以在一帧中被发送,你需要确保在接收端将它们混合起来,一个方法是在发送数据包前给它们添加一个小说明,表示它们是数据包还是聊天数据包,看一下下面的代码,其中D表示一个数据包,C表示一个聊天数据包:

writer.Write("D");
writer.Write(gameTime.TotalGameTime.Minutes);
writer.Write(gameTime.TotalGameTime.Seconds);LocalNetworkGamer localGamer = networkSession.LocalGamers[0];
localGamer.SendData(writer, SendDataOptions.ReliableInOrder);writer.Write("C");
writer.Write("This is a chat message from " + localGamer.Gamertag);
localGamer.SendData(writer, SendDataOptions.Chat|SendDataOptions.ReliableInOrder);

在接收端,只是简单地检查数据包是D还是C,并处理对应的数据包:

while (localGamer.IsDataAvailable)
...{NetworkGamer sender;localGamer.ReceiveData(reader, out sender);string messageType = reader.ReadString();if (messageType == "D")
...    {string gamerTime = "";gamerTime += sender.Gamertag + ": ";gamerTime += reader.ReadInt32() + "m ";gamerTime += reader.ReadInt32() + "s";gamerTimes[sender.Gamertag] = gamerTime;}else if (messageType == "C")
...    {lastChatMessage[sender.Gamertag] = reader.ReadString();}
}
多个本地玩家

如果多个玩家连接在同一个机器上,你需要通过迭代器发送和接受数据。

将数据发送到所有玩家很简单:

//send data from all local players to all other players in session
foreach (LocalNetworkGamer localGamer in networkSession.LocalGamers)
...{writer.Write(gameTime.TotalGameTime.Minutes); writer.Write(gameTime.TotalGameTime.Seconds);localGamer.SendData(writer, SendDataOptions.ReliableInOrder);
}

记住你可以使用SendData的一个重载方法将数据只发送到一个指定玩家。

接收数据也不难,只需循环代码直到所有本地玩家的IsDataAvailable为false:

foreach (LocalNetworkGamer localGamer in networkSession.LocalGamers) 
...{while (localGamer.IsDataAvailable)...{NetworkGamer sender;localGamer.ReceiveData(reader, out sender);string gamerTime = localGamer.Gamertag + " received from "; gamerTime += sender.Gamertag + ": ";gamerTime += reader.ReadInt32() + "m ";gamerTime += reader.ReadInt32() + "s";gamerTimes[sender.Gamertag] = gamerTime;}
}
代码

下面是Update方法的代码,包括扩展过的InSession状态,你的机器上的所有玩家将数据发送到会话中的所有玩家。然后,他们会接收发送给他们的数据。

如果你在多个机器上运行这个代码,他们会自动连接到第一个机器创建的会话上。然后,开始发送时间信息并在Draw方法中将接受到的数据显示在屏幕上。

protected override void Update(GameTime gameTime)
...{if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();if (this.IsActive)...{switch (currentGameState) ...{case GameState.SignIn:
...            {if (Gamer.SignedInGamers.Count < 1)
...                {Guide.ShowSignIn(1, false);log.Add("Opened User SignIn Interface");}else...{currentGameState = GameState.SearchSession;log.Add(Gamer.SignedInGamers[0].Gamertag + " logged in - proceed to SearchSession"); }}break;case GameState.SearchSession:
...            {AvailableNetworkSessionCollection activeSessions =  NetworkSession.Find(NetworkSessionType.SystemLink, 4, null);if (activeSessions.Count == 0)
...                {currentGameState = GameState.CreateSession;log.Add("No active sessions found - proceed to CreateSession");}else
...                {AvailableNetworkSession networkToJoin = activeSessions[0];networkSession = NetworkSession.Join(networkToJoin);string myString = "Joined session hosted by " + networkToJoin.HostGamertag; myString += " with " + networkToJoin.CurrentGamerCount.ToString() + " players"; myString += " and " + networkToJoin.OpenPublicGamerSlots.ToString() + " open player slots.";log.Add(myString);HookSessionEvents();currentGameState = GameState.InSession;}}break;case GameState.CreateSession:
...            {networkSession = NetworkSession.Create(NetworkSessionType.SystemLink, 4, 16);networkSession.AllowHostMigration = true; networkSession.AllowJoinInProgress = false; log.Add("New session created");HookSessionEvents();currentGameState = GameState.InSession;}break;case GameState.InSession:
...            {//send data from all local players to all other players in sessionforeach (LocalNetworkGamer localGamer in networkSession.LocalGamers)...{writer.Write(gameTime.TotalGameTime.Minutes); writer.Write(gameTime.TotalGameTime.Seconds); localGamer.SendData(writer, SendDataOptions.ReliableInOrder);}//receive data from all other players in sessionforeach (LocalNetworkGamer localGamer in networkSession.LocalGamers) ...{while (localGamer.IsDataAvailable)
...                    {NetworkGamer sender;localGamer.ReceiveData(reader, out sender);string gamerTime = localGamer.Gamertag + " received from "; gamerTime += sender.Gamertag + ": ";gamerTime += reader.ReadInt32() + "m ";gamerTime += reader.ReadInt32() + "s";gamerTimes[sender.Gamertag] = gamerTime;}}networkSession.Update();}break;}base.Update(gameTime);}
}

转载于:https://www.cnblogs.com/AlexCheng/archive/2011/03/07/2120083.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/298613.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务之 EShop on dapr概览

????欢迎点赞 &#xff1a;???? 收藏 ⭐留言 ???? 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;????本文作者&#xff1a;由webmote 原创&#xff0c;首发于 【掘金】????作者格言&#xff1a;生活在于折腾&#xff0c;当你不折…

苹果android 对比,苹果安卓旗舰差距有多少?看了这份对比,果粉傻眼了

最近&#xff0c;身边不少小伙伴都在换机&#xff0c;有的换了最新的 iPhone 12 系列&#xff0c;有的则是换成安卓旗舰&#xff0c;毕竟现在的安卓旗舰与 iPhone 之间的体验已经十分接近&#xff0c;甚至在一些方面安卓旗舰还有着不小的优势。下面&#xff0c;我们以最新的 iP…

python画画用哪库好_Python我要学画画-turtle库

上帝说&#xff1a;“要有光&#xff01;” 于是&#xff0c;就有了光。 ---《圣经》旧约创世纪篇 我要学画画&#xff0c;Python便有了turtle库。 turtle库是一个点线面的简单图像库。画布中心为坐标系原点&#xff0c;小海龟起始位置就在原点方向向右。turtle界面 Python与库…

看完这15张动图,秒懂万有引力与航天难点!

全世界只有3.14 % 的人关注了爆炸吧知识椭圆的画法大质量天体使周围天体绕其运转模拟太阳系星球轨迹非常接近圆火箭运载卫星升空卫星飞行过程中可以点火调整姿态同步卫星必须在赤道上空北斗全球卫星导航轨道半径越大&#xff0c;卫星越慢人类发射的卫星越来越多嫦娥沿椭圆轨道奔…

物联网工程专业的迷茫与抉择

大家好&#xff0c;我是阿辉&#xff0c;很高兴在这里和你讲述所思所想。周末了&#xff0c;就聊点比较轻松的话题。正文共1633字&#xff0c;预计阅读时间5分钟。想必很多朋友是通过#毕业四年&#xff0c;我当初是如何走上编程这条路的&#xff01;#这篇文章才熟知我的吧&…

html二级页面内容滑动,jQuery+CSS实现的网页二级下滑菜单效果

本文实例讲述了jQueryCSS实现的网页二级下滑菜单效果。分享给大家供大家参考。具体如下&#xff1a;这是一款简洁型的 jQueryCSS网页二级下滑菜单&#xff0c;练手写的&#xff0c;有需要的自己拿去美化吧&#xff0c;基本的动画效果和菜单下滑效果和渐变效果已经做出来了&…

parentElement,srcElement 使用

代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><meta http-equiv"Content-T…

哈哈哈,程序员没有女朋友的原因,我终于找到了!

全世界只有3.14 % 的人关注了爆炸吧知识程序员没有女朋友的原因▼程序员大脑里想的▼每天要学习太多语言&#xff0c;程序员太忙了▼女朋友 VS 编译器▼程序员sao起来&#xff0c;还需要女朋友吗&#xff1f;▼电脑才是程序员的女朋友▼互道晚安后&#xff0c;会不会偶遇在同一…

基于事件驱动架构构建微服务第7部分:在仓储上实现事件溯源

原文链接&#xff1a;https://logcorner.com/building-microservices-through-event-driven-architecture-part7-implementing-eventsourcing-on-repositories/在本文中&#xff0c;我将讨论Repository上的Event Sourcing实现。仓储负责将事件添加到事件存储并从事件存储中检索…

python选择题题目_Python接口测试题(持续更新中)

1、json和字典的区别&#xff1f; Json是轻量级的数据交互格式&#xff0c;以key-value的键值对形式来保存数据&#xff0c;结构清晰&#xff0c;可以说是目前互联网项目开发中最常用的一种数据交互格式。 字典&#xff0c;同样是以key-value的键值对来保存数据&#xff0c;是p…

html5 ajax数据显示,html5的ajax学习(二)

一、基础知识点1.ajax可以做事情&#xff1a;局部刷新 浏览器搜索列表记录 加载更多的数据2.登录页面同步网络请求&#xff1a;页面全部刷新&#xff0c;用户量大体验很不好3.ajax的详解&#xff1a;ajax的get和post请求 同步还是异步&#xff0c;true为异步ajax.open("ge…

ISA Server服务器故障恢复一例系统盘符更换之后的应对方法

周四下午的时候&#xff0c;某政府信息中心领导打电话告诉我&#xff0c;ISA Server服务器不能开机了。随后公司的技术员到达现场&#xff0c;经过检查&#xff0c;发现服务器显卡损坏。在更换显卡后&#xff0c;服务器可以开机&#xff0c;但却不能进入系统—-服务器在经过BIO…

扩展Windows Mobile模拟器存储空间的方法

在Windows Mobile应用程序开发的初期&#xff0c;可以使用SDK自带的模拟器来进行调试&#xff0c;这给我们开发人员提供了一种方便的途径。一般的应用程序&#xff0c;占用空间的大小也就几 百K&#xff0c;或者几M&#xff0c;这在模拟器上调试起来一点问题也没有。但是有的时…

UOS LoongArch 上成功安装.NET Core 3.1

龙芯.NET团队正式发布了.NET Core 3.1 For LoongArch, 具体参见龙芯开源网站 http://www.loongnix.cn/index.php/Dotnet 。进入安装包下载地址LoongArch64-.NET Core 3.1&#xff0c;可以看到龙芯.NET团队做了很多工作&#xff0c;为Debian和Redhat两大Linux体系平台都做好了基…

c++ vector拷贝构造_JDK源码分析-Vector

1. 概述上文「JDK源码分析-ArrayList」主要分析了 ArrayList 的实现原理。本文分析 List 接口的另一个实现类&#xff1a;Vector。Vector 的内部实现与 ArrayList 类似&#xff0c;也可以理解为一个「可变数组」。其继承结构如下&#xff08;省略部分接口&#xff09;&#xff…

除了PS,原来这个也可以轻松实现图像处理!

全世界只有3.14 % 的人关注了爆炸吧知识在我们生活中&#xff0c;常见的图像处理软件有Adobe Photoshop、Adobe Illustrator等。然而&#xff0c;并非只有软件才能实现图像处理&#xff0c;通过编程手段也是能实现的&#xff01;今天&#xff0c;小天将要带着大家走进计算机视觉…

下雨天纵使少了什么也是少不了一把伞的&#xff0c;即使是几千年前&#xff0c;也不管细雨霏霏&#xff0c;大雨倾盆。愿意沐浴风雨中&#xff0c;享受这样惠泽的人总是少的。从“孤舟蓑笠翁&#xff0c;独钓寒江雪”&#xff0c;“十里一长亭&#xff0c;五里一短亭”&#xf…

oracle用户名密码过期引起的网站后台无法登录

本来今天休息&#xff0c;但是接到同事反映&#xff1a;客户的WEB无法登录了&#xff0c;网站能打开&#xff0c;但是后台登录不了。我就联系了客户&#xff0c;客户说是WEB用户的密码过期导致的&#xff0c;默认是180天到期。接着就是我的操作流程了&#xff1a;1.先从WEB服务…

微服务的终极目标,Mecha分布式运行时之Dapr

1. Mecha 是啥&#xff1f;微服务发展到今天&#xff0c;已经有很多公司多年前已经改造完毕&#xff0c;也有些公司还在路上&#xff0c;微服务的优势&#xff0c;有过了解的朋友应该也都能说出个一二三来&#xff0c;经历过微服务改造的&#xff0c;应该都知道其中的艰辛。单体…

python input输入多个变量_「Python 秘籍」1.2 解压可迭代对象赋值给多个变量

问题需要从某个可迭代对象中分解出 N 个元素&#xff0c;但是这个可迭代对象的长度可能超过 N&#xff0c;这会导致出现“需要解包的值过多(too many values to unpack)”的异常。解决方案“星号表达式”可以用来解决这个问题。例如&#xff0c;假设开设了一门课程&#xff0c;…