当前位置: 首页 > 编程语言 > C#

通过tcp或套接字共享发送类型对象

时间:2023-04-11 11:33:49 C#

通过tcp或套接字发送类型对象我在为使用Xna制作的非常简单的游戏创建Web界面时遇到了问题。我只需要通过TCP客户端/套接字发送对象。示例:我有一个名为“Player”的类。在每个玩家中,都有一个名为“Info”的字段,类型为“PlayerInfo”。在客户端/服务器中,我需要将每个玩家的信息发送给每个客户端,除了发送它的人(显然)。这是一个简单的例子,但我需要用大约5-10个对象来做,再加上发送播放器更新(位置、动作......)有没有一种简单的方法可以用TCP/Sock来做到这一点?注意:我会将我在C#和编程方面的知识评为6/10,因此如果您有解决方案,则无需解释所有内容(例如:变量和字段之间的区别是什么)。我也知道接口、库等......提前致谢!我有一种方法可以推荐,两种较小的方法取决于很多东西。第一个提示您已经知道如何使用Socket类,但您需要通过它发送许多类。从传输的角度来看,您应该创建/考虑一个非常简单的类。我们称这个类为MyMessage:publicclassMyMessage{publicbyte[]Data{get;放;}}好的。从TCP的角度来看,您需要做的就是确保能够传递此类的实例(从客户端到服务器再返回)。我不会详细介绍这样做的细节,但我会指出,如果您设法做到这一点,您就会将TCP/IP连接的性质从“字节流”更改为“消息流”。这意味着一般来说,TCP/IP不保证您通过连接发送的数据块以相同的格式到达目的地(它们可能被连接或拆分)。它唯一保证的是所有块的字节最终将以相同的顺序(总是)到达连接的另一端。现在您已经启动并运行了消息流,您可以使用.NET良好的旧序列化将任何类实例封装在Data属性中。它所做的是将对象图序列化为字节,反之亦然。您(最常见)使用标准库类执行此操作:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter,在mscorlib.dll中找到,如下所示:publicstaticclassFoo{publicstaticMessageSerialize(objectanySerializableObject){varmemoryStream=newMemoryStream()){(newBinaryFormatter()).Serialize(memoryStream,anySerializableObject);返回新消息{Data=memoryStream.ToArray()};}}publicstaticobjectDeserialize(Messagemessage){使用(varmemoryStream=newMemoryStream(message.Data))return(newBinaryFormatter()).Deserialize(memoryStream);BinaryFormatter类能够遍历从根/Sentinel开始的对象的树/图,并将所有原始值加上类型信息和相对位置信息写入提供的流。只要提供的流对应于先前对象图被序列化的位置,它也能够完全反转和反序列化整个对象图。这里有一些问题:您需要用[SerializableAttribute]注释所有类。如果您的类包含您编写的其他类的字段,并且您说它们包含:[SerializableAttribute]publicclassPlayer{publicPlayerInfoInfo;//...等然后你需要用[SerializableAttribute]注释那些:[SerializableAttribute]publicclassPlayerInfo{//...等使用此属性注释这些字段。大多数可以序列化是的。原始类型自然是可序列化的。不应序列化的事物有:FileStreams、Threads、Sockets等。在确保你有可序列化的类之后,你需要做的就是序列化它们的实例,发送它们,接收它们并反序列化它们:序列化(移动);socketHelper.SendMessage(消息);}publicstaticvoidSendPlayer(Playerplayer){Messagemessage=Foo.Serialize(player);socketHelper.SendMessage(消息);}//..等publicstaticvoidOnMessageReceivedFromServer(Messagemessage){objectobj=Foo.Deserialize(message);if(objisMovement)Client.ProcessOtherPlayersMovement(objasMovement);elseif(objisPlayer)Client.ProcessOtherPlayersStatusUpdates(objasPlayer);//..等}publicstaticvoidProcessOtherPlayersMovement(Movementmovement){//...}//..等}在服务器端:classServer{publicstaticvoidOnMessageReceived(Messagemessage,SocketHelperfrom,SocketHelper[]all){objectobj=Foo.Deserialize(message);if(objisMovement)Server.ProcessMovement(objasMovement);elseif(objisPlayer)Server.ProcessPlay呃(obj作为玩家);//..etcforeach(varsocketHelperinall)if(socketHelper!=from)socketHelper.SendMessage(message);您将需要一个两个可执行项目(客户端和服务器)引用公共程序集项目(类库)您需要传递的所有类都必须在该程序集中编写,以便服务器和客户端都知道如何在这个时候相互理解详细程度。如果服务器不需要了解客户端之间发生的事情,而只是传递消息(向其他N-1客户端广播消息),那么请忘记我所说的通用程序集。在这种特殊情况下,服务器只看到字节,而客户端对来回发送的实际消息有更深入的了解。我说我有三种方法。第二个涉及.NETRemoting,这可能需要您做很多工作,但如果您不完全理解它就很难接受。您可以在MSDN上阅读它,在这里:http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx只有当(或现在或将来)第三个才会更好)XNA你的意思是WindowsPhone或不支持BinaryFormatter类的XNA的另一个实现(带有MonoTouch的ExEn,或其他)。在这种情况下,如果您需要您的服务器(一个成熟的、老式的.NET应用程序)来引用我正在谈论的公共程序集并且还有游戏项目(这不会是好的老式的),您可以将是困难的。NET应用程序,但具有相当奇特的性质)指的是完全相同的程序集。在这种情况下,我们需要使用和替换窗体的序列化和反序列化对象。您还需要在两个世界(.NET和WP7或WP8)中以相同的方式实现两组类。您可以使用某种形式的XML序列化程序,您需要将它们显式映射到您的类(不如BinaryFormatter类强大,但在运行时托管类的性质上更通用)。您可以在此处阅读有关MSDN上的XmlSerializer类的信息:http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx我个人使用JSON.NET的快速而干净的解决方案:类JMessage{公共类型类型{得到;放;}公共JToken值{得到;放;}publicstaticJMessageFromValue(Tvalue){returnnewJMessage{Type=typeof(T),Value=JToken.FromObject(value)};}publicstaticstringSerialize(JMessagemessage){returnJToken.FromObject(message).ToString();}publicstaticJMessageDeserialize(stringdata){returnJToken.Parse(data).ToObject();现在你可以像这样序列化对象:Playerplayer=...;敌人敌人=...;stringdata1=JMessage.Serialize(JMessage.FromValue(player));stringdata2=JMessage.Serialize(JMessage.FromValue(enemy));通过线路发送数据,然后在另一端你可以做这样的事情:stringdata=...;JMessage消息=JMessage.Deserialize(数据);if(message.Type==typeof(Player)){Playerplayer=message.Value.ToObject();}elseif(message.Type==typeof(Enemy)){敌人enemy=消息.Value.ToObject();}//etc...您可以使用.net框架中可用的各种类创建您自己的解决方案您可能想查看WCF或套接字命名空间,尤其是TcpClient和TcpListener类,请参阅MSDN。如果您搜索与使用这些教程相关的内容,可以找到很多很棒的教程。您还需要考虑如何将类型化对象转换为字节数组,类似于此问题。另一种方法是使用网络库。有低级库和高级库。鉴于您的编程经验水平和特定的最终目标,我建议使用高级库。这种网络库的一个例子是Lids。我是另一个网络库networkComms.net的开发人员,下面是一个如何使用该库发送类型对象的快速示例:共享基础(定义播放器对象):[ProtoContract]classPlayer{[ProtoMember(1)]公共字符串名称{得到;私有集;}[ProtoMember(2)]publicintAmmo{get;私有集;}[ProtoMember(3)]publicstringPosition{get;私有集;}privatePlayer(){}publicPlayer(stringname,intammo,stringposition){this.Name=name;this.Ammo=弹药;this.Position=位置;}}客户端(发送单个Player对象):使用System;使用System.Collections.Generic;使用系统。林克;使用系统文本;使用System.IO;使用NetworkCommsDotNet;使用ProtoBuf;namespaceClient{classProgram{staticvoidMain(string[]args){Playerplayer=newPlayer("MarcF",100,"09.09N,21.12W");//也可以使用UDPConnection.GetConnection...TCPConnection.GetConnection(newConnectionInfo("127.0.0.1",10000)).SendObject("PlayerData",player);Console.WriteLine("发送完成,按任意键退出客户端。");安慰。读键(真);rkComms.Shutdown();服务器:使用系统;使用System.Collections.Generic;使用System.Linq;使用系统文本;使用System.IO;使用NetworkCommsDotNet;使用ProtoBuf;namespaceServer{classProgram{staticvoidMain(string[]args){//将传入数据转换为对象并在收到传入数据包时运行此方法。NetworkComms.AppendGlobalIncomingPacketHandler("PlayerData",(packetHeader,connection,incomingPlayer)=>{Console.WriteLine("Receivedplayerdata.Playernamewas"+incomingPlayer.Name);//在这里对播放器对象做任何其他操作//例如UpdatePlayerPosition(incomingPlayer);});//侦听传入连接TCPConnection.StartListening(true);安慰。WriteLine("服务器准备就绪。按任意键关闭服务器。");控制台.ReadKey(true);NetworkComms.Shutdown();}}}以上是本教程的修改版本您显然需要从网站下载NetworkCommsDotNetDLL,以便您可以将其添加到“使用NetworkCommsDotNet”参考中。另请参阅客户端示例中的服务器IP地址当前为“127.0.0.1”,如果您在同一台计算机上同时运行服务器和客户端,这应该有效。2年多后,我找到了这个问题的新解决方案,并认为有人分享它可能会有用。请注意,已接受的答案仍然有效。序列化类型对象的最简单方法是使用Json.NET中的json转换器。有一个设置对象允许您将类型存储在json中作为一个名为$type的值。以下是操作方法和生成的json:JsonSerializerSettingssettings=newJsonSerializerSettings{TypeNameHandling=TypeNameHandling.All};JsonConvert.SerializeObject(myObject,设置);Json结果:{"$type":"Testing.MyType,Testing","ExampleProperty":"Helloworld!"反序列化时,使用相同的设置将反序列化正确类型的对象。正是我需要的!希望这可以帮助。以上就是C#学习教程:通过tcp或s??ockets发送类型对象分享的全部内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: