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

时间:2022-12-26 23:57:40

I’m having trouble creating a network interface for a very simple game I’ve made in Xna. I would simply need to send objects through a TCP client / Socket. Ex: I have a class named “Player”. In every Player, there’s a field name “Info”, of type “PlayerInfo”. In the client / server, I would need to send every players’ info to every client except the one who sent it (obviously). This is a simple example, but I would need to do that with around 5 – 10 objects, plus sending the player updates (positions, actions …) Is there a simple way to do that with TCP / Sock? Note : I would rate my knowledge in C# and programming as 6/10, so you don’t need to explain everything if you have a solution (Ex : what’s the difference between a variable and a field). I also know about interfaces, libraries and so on… Thanks in advance!

我在为Xna制作的非常简单的游戏创建网络界面时遇到了麻烦。我只需要通过TCP客户端/ Socket发送对象。例如:我有一个名为“玩家”的班级。在每个播放器中,都有一个字段名称“Info”,类型为“PlayerInfo”。在客户端/服务器中,我需要将每个玩家的信息发送给每个客户,除了发送它的人(显然)。这是一个简单的例子,但是我需要用大约5到10个对象来做,再加上发送玩家更新(位置,动作......)有没有一种简单的方法可以用TCP / Sock做到这一点?注意:我会将我的知识用C#和编程评为6/10,所以如果你有解决方案,你不需要解释所有内容(例如:变量和字段之间有什么区别)。我也知道接口,库等...提前谢谢!

4 个解决方案

#1


25  

I have one approach I would recommend and two lesser ones which are dependent on many things.

我有一种方法可以推荐,两种较小的方法依赖于很多东西。

The first one implies you already know how to use the Socket class but have a lot of classes that you need to send through it.

第一个暗示您已经知道如何使用Socket类,但是您需要通过它发送许多类。

From a transport point of view you should create / take into consideration just one very simple class. Let's call this class MyMessage:

从传输的角度来看,您应该创建/考虑一个非常简单的类。我们称这个类为MyMessage:

public class MyMessage {
  public byte[] Data { get; set; }
}

Ok. From a TCP point of view all you need to do is make sure you're able to pass instances of this class around (from clients to the server and back). I will not dive into the details of doing that but I will point out that if you manage to do this you transform the nature of the TCP/IP connection from "byte-stream" to "message-stream". What that means is that normally, TCP/IP does not guarantee that chunks of data you send through a connection arrive at the destination in the same formations (they might get joined together or be split apart). The only thing it guarantees is that the bytes of all the chunks will eventually arrive in the same order at the other end of the connection (always).

好。从TCP的角度来看,您需要做的就是确保您能够传递此类的实例(从客户端到服务器并返回)。我不会深入研究这样做的细节,但我会指出,如果你设法做到这一点,你将TCP / IP连接的性质从“字节流”转换为“消息流”。这意味着通常情况下,TCP / IP不保证您通过连接发送的数据块在相同的格式中到达目的地(它们可能会连接在一起或被拆分)。它唯一保证的是所有块的字节最终将以相同的顺序到达连接的另一端(总是)。

Now that you have a message-stream up and running you could use .NET good old serialization to encapsulate any class instance inside the Data property. What that does is it serializes object graphs into bytes and vice-versa.

现在您已启动并运行消息流,您可以使用.NET良好的旧序列化来封装Data属性中的任何类实例。它的作用是将对象图序列化为字节,反之亦然。

The way you do that (most commonly) is to use the standard library class: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter which can be found in mscorlib.dll like so:

你最常用的方法是使用标准库类:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter,它可以在mscorlib.dll中找到,如下所示:

public static class Foo {

  public static Message Serialize(object anySerializableObject) {
    using (var memoryStream = new MemoryStream()) {
      (new BinaryFormatter()).Serialize(memoryStream, anySerializableObject);
      return new Message { Data = memoryStream.ToArray() };
    }
  }

  public static object Deserialize(Message message) {
    using (var memoryStream = new MemoryStream(message.Data))
      return (new BinaryFormatter()).Deserialize(memoryStream);
  }

}

The BinaryFormatter class is able to traverse the tree/graph of objects starting from the root / sentinel provided as the second argument of the Serialize (Stream, object) method and write all of the primitive values plus type information and relative position information to the provided stream. It is also able to do the exact reverse and deserialize an entire object graph as long as the provided stream is positioned accordingly to the place of a former object graph serialization.

BinaryFormatter类能够遍历从作为Serialize(Stream,object)方法的第二个参数提供的root / sentinel开始的对象的树/图,并将所有原始值加上类型信息和相对位置信息写入提供的流。只要提供的流相应于前一个对象图序列化的位置,它也能够完全反向并反序列化整个对象图。

There are a few catches here though: you will need to annotate all of your classes with [SerializableAttribute]. If your classes contain fields that are of other classes written by you, and you said they do:

这里有一些捕获:您需要使用[SerializableAttribute]注释所有类。如果您的类包含由您编写的其他类的字段,并且您说他们这样做:

[SerializableAttribute]
public class Player {
  public PlayerInfo Info; 
  //... etc 

then you need to annotate those with [SerializableAttribute] too:

那么你需要用[SerializableAttribute]注释那些:

[SerializableAttribute]
public class PlayerInfo { //... etc

If your classes contain fields that are of types written by others (say Microsoft) then those would better be already annotated with the attribute. Most of those which could be serialized already are. Primitive types are naturally serializable. Things that shouldn't be serialized are: FileStreams, Threads, Sockets, etc.

如果您的类包含由其他人(比如Microsoft)编写的类型的字段,则最好使用该属性对这些字段进行注释。大多数可以序列化的是。原始类型是自然可序列化的。不应该序列化的东西是:FileStreams,Threads,Sockets等。

After making sure you have serializable classes all you need to do is serialize their instances, send them, receive them and deserialize them:

在确保您具有可序列化的类之后,您需要做的就是序列化它们的实例,发送它们,接收它们并反序列化它们:

class Client {

  public static void SendMovement(Movement movement) {
    Message message = Foo.Serialize(movement);

    socketHelper.SendMessage(message);
  }
  public static void SendPlayer(Player player) {
    Message message = Foo.Serialize(player);

    socketHelper.SendMessage(message);
  }
  // .. etc

  public static void OnMessageReceivedFromServer(Message message) {
    object obj = Foo.Deserialize(message);
    if (obj is Movement)
      Client.ProcessOtherPlayersMovement(obj as Movement);
    else if (obj is Player)
      Client.ProcessOtherPlayersStatusUpdates(obj as Player);
    // .. etc
  }

  public static void ProcessOtherPlayersMovement(Movement movement) {
    //...
  }
  // .. etc

}

While on the server side:

在服务器端:

class Server {

  public static void OnMessageReceived(Message message, SocketHelper from, SocketHelper[] all) {
    object obj = Foo.Deserialize( message );
    if (obj is Movement)
      Server.ProcessMovement( obj as Movement );
    else if (obj is Player)
      Server.ProcessPlayer( obj as Player );
    // .. etc

    foreach (var socketHelper in all)
      if (socketHelper != from)
        socketHelper.SendMessage( message );
  }
}

You will need a common assembly project (class library) to be referenced by both executable projects (client and server).

您将需要一个可由两个可执行项目(客户端和服务器)引用的公共程序集项目(类库)。

All your classes that need to be passed around will have to be written in that assembly so as that both the server and the client know how to understand each other at this very detailed level.

您需要传递的所有类都必须在该程序集中编写,以便服务器和客户端都知道如何在这个非常详细的级别上相互理解。

If the server needs not understand what is being said between the clients and only pass around messages (broadcasting one message to the other N-1 clients) then forget what I said about the common assembly. In that particular case, the server sees only bytes, while the clients have a deeper understanding of the actual messages being sent back and forth.

如果服务器不需要理解客户端之间的内容,只传递消息(向其他N-1客户端广播一条消息),那么就忘记我对常用程序集的说法了。在该特定情况下,服务器仅看到字节,而客户端对来回发送的实际消息有更深入的了解。

I said I had three approaches.

我说我有三种方法。

The second one involves .NET Remoting which can take a lot of work off your shoulders but which is hard to live with if you don't fully understand it. You can read about it on MSDN, here: http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx

第二个涉及.NET Remoting,它可能需要你的大量工作,但如果你不完全理解它,很难接受。你可以在MSDN上阅读它,在这里:http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx

The third one would be better only if (now, or in the future) by XNA you mean Windows Phone or another implementation of XNA which does not support the BinaryFormatter class (ExEn with MonoTouch, or others). In that case you would have a hard time if you needed your server (a full blown, good old fashioned .NET application) to reference the common assembly I talked about and also have the game project (which would not be a good old fashioned .NET app but have a rather exotic nature) reference the exact same assembly.

第三个会更好,只有当(或现在或将来)XNA你的意思是Windows Phone或XNA的另一个实现,它不支持BinaryFormatter类(ExEn与MonoTouch,或其他)。在这种情况下,如果你需要你的服务器(一个完整的,老式的.NET应用程序)来引用我所谈论的公共程序集并且还有游戏项目(这将不是一个很好的老式),你会很难。 NET应用程序但具有相当奇特的性质)引用完全相同的程序集。

In that case we would need to use and alternate form of serializing and deserializing your objects. You would also need to identically implement two sets of classes in the two worlds (.NET and WP7 or WP8). You could use some form of XML serializers which you would need to map to your classes explicitly (not as powerful as the BinaryFormatter class but more versatile in what the nature of the runtime that host your classes could be).

在这种情况下,我们需要使用和替换形式的序列化和反序列化对象。您还需要在两个世界(.NET和WP7或WP8)中相同地实现两组类。您可以使用某种形式的XML序列化程序,您需要将它们显式映射到您的类(不像BinaryFormatter类那样强大,但在托管类的运行时的性质方面更为通用)。

You can read about the XmlSerializer class on MSDN, here: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

您可以在MSDN上阅读有关XmlSerializer类的信息,请访问:http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

#2


8  

My personal quick-and-clean solution, using JSON.NET:

我的个人快速清洁解决方案,使用JSON.NET:

class JMessage
{
    public Type Type { get; set; }
    public JToken Value { get; set; }

    public static JMessage FromValue<T>(T value)
    {
        return new JMessage { Type = typeof(T), Value = JToken.FromObject(value) };
    }

    public static string Serialize(JMessage message)
    {
        return JToken.FromObject(message).ToString();
    }

    public static JMessage Deserialize(string data)
    {
        return JToken.Parse(data).ToObject<JMessage>();
    }
}

Now you can serialize your objects like so:

现在您可以像这样序列化对象:

Player player = ...;
Enemy enemy = ...;
string data1 = JMessage.Serialize(JMessage.FromValue(player));
string data2 = JMessage.Serialize(JMessage.FromValue(enemy));

Send that data across the wire and then on the other end you can do something like:

通过线路发送数据,然后在另一端,您可以执行以下操作:

string data = ...;
JMessage message = JMessage.Deserialize(data);
if (message.Type == typeof(Player))
{
    Player player = message.Value.ToObject<Player>();
}
else if (message.Type == typeof(Enemy))
{
    Enemy enemy = message.Value.ToObject<Enemy>();
}
//etc...

#3


3  

You could create your own solution using the various classes provided in the .net framework. You would want to checkout WCF or the Sockets namepsace, specifically the TcpClient and TcpListener classes, see MSDN. There are loads of great tutorials if you do a search related to using these. You would also need to consider how to turn your typed objects into byte arrays, similar to this question.

您可以使用.net框架中提供的各种类创建自己的解决方案。您可能想要签出WCF或套接字namepsace,特别是TcpClient和TcpListener类,请参阅MSDN。如果您进行与使用这些相关的搜索,那么有很多很棒的教程。您还需要考虑如何将类型化对象转换为字节数组,类似于这个问题。

An alternative approach would be to use a network library. There are low-level libraries and high-level libraries. Given your level of programming experience and specific end goal I would suggest a high-level library. An example of such a network library would be lidgren. I'm the developer of another network library networkComms.net and a quick example of how you can send typed objects using this library follows:

另一种方法是使用网络库。有低级库和高级库。鉴于您的编程经验水平和特定的最终目标,我建议使用高级库。这种网络库的一个例子就是盖子。我是另一个网络库networkComms.net的开发人员,以及如何使用此库发送类型对象的快速示例如下:

Shared Base (defines Player object):

共享基础(定义播放器对象):

[ProtoContract]
class Player
{
    [ProtoMember(1)]
    public string Name { get; private set; }
    [ProtoMember(2)]
    public int Ammo { get; private set; }
    [ProtoMember(3)]
    public string Position { get; private set; }

    private Player() { }

    public Player(string name, int ammo, string position)
    {
        this.Name = name;
        this.Ammo = ammo;
        this.Position = position;
    }
}

Client (sends a single Player object):

客户端(发送单个Player对象):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using NetworkCommsDotNet;
using ProtoBuf;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player("MarcF", 100, "09.09N,21.12W");

            //Could also use UDPConnection.GetConnection...
            TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000)).SendObject("PlayerData", player);

            Console.WriteLine("Send completed. Press any key to exit client.");
            Console.ReadKey(true);
            NetworkComms.Shutdown();
        }
    }
}

Server:

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using NetworkCommsDotNet;
using ProtoBuf;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            // Convert incoming data to a <Player> object and run this method when an incoming packet is received.
            NetworkComms.AppendGlobalIncomingPacketHandler<Player>("PlayerData", (packetHeader, connection, incomingPlayer) =>
            {
                Console.WriteLine("Received player data. Player name was " + incomingPlayer.Name);
                //Do anything else with the player object here
                //e.g. UpdatePlayerPosition(incomingPlayer);
            });

            //Listen for incoming connections
            TCPConnection.StartListening(true);

            Console.WriteLine("Server ready. Press any key to shutdown server.");
            Console.ReadKey(true);
            NetworkComms.Shutdown();
        }
    }
}

The above is a modified version of this tutorial. You will obviously need to download the NetworkCommsDotNet DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.

以上是本教程的修改版本。您显然需要从网站下载NetworkCommsDotNet DLL,以便您可以在“使用NetworkCommsDotNet”参考中添加它。另请参阅客户端示例中的服务器IP地址当前为“127.0.0.1”,如果您在同一台计算机上同时运行服务器和客户端,这应该可以工作。

#4


1  

After more than 2 years, I've found new ways of solving this issue, and I thought sharing it might be useful to someone. Please note the accepted answer is still valid.

经过两年多的时间,我找到了解决这个问题的新方法,我认为分享它可能对某些人有用。请注意,接受的答案仍然有效。

A simplest way to serialize typed objects I could find is by using the json converter in Json.NET. There's a settings object that allow you to store the type in the json as a value named $type. Here's how to do it and the resulting json:

序列化类型对象的最简单方法是在Json.NET中使用json转换器。有一个设置对象,允许您将类型存储在json中作为名为$ type的值。以下是如何执行此操作以及生成的json:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

JsonConvert.SerializeObject(myObject, settings);

Json result:

Json结果:

{
    "$type" : "Testing.MyType, Testing",
    "ExampleProperty" : "Hello world!"
}

When deserializing, if the same setting is used, an object of the correct type will be deserialized. Exactly what I needed! Hope this helps.

反序列化时,如果使用相同的设置,则将反序列化正确类型的对象。正是我需要的!希望这可以帮助。

#1


25  

I have one approach I would recommend and two lesser ones which are dependent on many things.

我有一种方法可以推荐,两种较小的方法依赖于很多东西。

The first one implies you already know how to use the Socket class but have a lot of classes that you need to send through it.

第一个暗示您已经知道如何使用Socket类,但是您需要通过它发送许多类。

From a transport point of view you should create / take into consideration just one very simple class. Let's call this class MyMessage:

从传输的角度来看,您应该创建/考虑一个非常简单的类。我们称这个类为MyMessage:

public class MyMessage {
  public byte[] Data { get; set; }
}

Ok. From a TCP point of view all you need to do is make sure you're able to pass instances of this class around (from clients to the server and back). I will not dive into the details of doing that but I will point out that if you manage to do this you transform the nature of the TCP/IP connection from "byte-stream" to "message-stream". What that means is that normally, TCP/IP does not guarantee that chunks of data you send through a connection arrive at the destination in the same formations (they might get joined together or be split apart). The only thing it guarantees is that the bytes of all the chunks will eventually arrive in the same order at the other end of the connection (always).

好。从TCP的角度来看,您需要做的就是确保您能够传递此类的实例(从客户端到服务器并返回)。我不会深入研究这样做的细节,但我会指出,如果你设法做到这一点,你将TCP / IP连接的性质从“字节流”转换为“消息流”。这意味着通常情况下,TCP / IP不保证您通过连接发送的数据块在相同的格式中到达目的地(它们可能会连接在一起或被拆分)。它唯一保证的是所有块的字节最终将以相同的顺序到达连接的另一端(总是)。

Now that you have a message-stream up and running you could use .NET good old serialization to encapsulate any class instance inside the Data property. What that does is it serializes object graphs into bytes and vice-versa.

现在您已启动并运行消息流,您可以使用.NET良好的旧序列化来封装Data属性中的任何类实例。它的作用是将对象图序列化为字节,反之亦然。

The way you do that (most commonly) is to use the standard library class: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter which can be found in mscorlib.dll like so:

你最常用的方法是使用标准库类:System.Runtime.Serialization.Formatters.Binary.BinaryFormatter,它可以在mscorlib.dll中找到,如下所示:

public static class Foo {

  public static Message Serialize(object anySerializableObject) {
    using (var memoryStream = new MemoryStream()) {
      (new BinaryFormatter()).Serialize(memoryStream, anySerializableObject);
      return new Message { Data = memoryStream.ToArray() };
    }
  }

  public static object Deserialize(Message message) {
    using (var memoryStream = new MemoryStream(message.Data))
      return (new BinaryFormatter()).Deserialize(memoryStream);
  }

}

The BinaryFormatter class is able to traverse the tree/graph of objects starting from the root / sentinel provided as the second argument of the Serialize (Stream, object) method and write all of the primitive values plus type information and relative position information to the provided stream. It is also able to do the exact reverse and deserialize an entire object graph as long as the provided stream is positioned accordingly to the place of a former object graph serialization.

BinaryFormatter类能够遍历从作为Serialize(Stream,object)方法的第二个参数提供的root / sentinel开始的对象的树/图,并将所有原始值加上类型信息和相对位置信息写入提供的流。只要提供的流相应于前一个对象图序列化的位置,它也能够完全反向并反序列化整个对象图。

There are a few catches here though: you will need to annotate all of your classes with [SerializableAttribute]. If your classes contain fields that are of other classes written by you, and you said they do:

这里有一些捕获:您需要使用[SerializableAttribute]注释所有类。如果您的类包含由您编写的其他类的字段,并且您说他们这样做:

[SerializableAttribute]
public class Player {
  public PlayerInfo Info; 
  //... etc 

then you need to annotate those with [SerializableAttribute] too:

那么你需要用[SerializableAttribute]注释那些:

[SerializableAttribute]
public class PlayerInfo { //... etc

If your classes contain fields that are of types written by others (say Microsoft) then those would better be already annotated with the attribute. Most of those which could be serialized already are. Primitive types are naturally serializable. Things that shouldn't be serialized are: FileStreams, Threads, Sockets, etc.

如果您的类包含由其他人(比如Microsoft)编写的类型的字段,则最好使用该属性对这些字段进行注释。大多数可以序列化的是。原始类型是自然可序列化的。不应该序列化的东西是:FileStreams,Threads,Sockets等。

After making sure you have serializable classes all you need to do is serialize their instances, send them, receive them and deserialize them:

在确保您具有可序列化的类之后,您需要做的就是序列化它们的实例,发送它们,接收它们并反序列化它们:

class Client {

  public static void SendMovement(Movement movement) {
    Message message = Foo.Serialize(movement);

    socketHelper.SendMessage(message);
  }
  public static void SendPlayer(Player player) {
    Message message = Foo.Serialize(player);

    socketHelper.SendMessage(message);
  }
  // .. etc

  public static void OnMessageReceivedFromServer(Message message) {
    object obj = Foo.Deserialize(message);
    if (obj is Movement)
      Client.ProcessOtherPlayersMovement(obj as Movement);
    else if (obj is Player)
      Client.ProcessOtherPlayersStatusUpdates(obj as Player);
    // .. etc
  }

  public static void ProcessOtherPlayersMovement(Movement movement) {
    //...
  }
  // .. etc

}

While on the server side:

在服务器端:

class Server {

  public static void OnMessageReceived(Message message, SocketHelper from, SocketHelper[] all) {
    object obj = Foo.Deserialize( message );
    if (obj is Movement)
      Server.ProcessMovement( obj as Movement );
    else if (obj is Player)
      Server.ProcessPlayer( obj as Player );
    // .. etc

    foreach (var socketHelper in all)
      if (socketHelper != from)
        socketHelper.SendMessage( message );
  }
}

You will need a common assembly project (class library) to be referenced by both executable projects (client and server).

您将需要一个可由两个可执行项目(客户端和服务器)引用的公共程序集项目(类库)。

All your classes that need to be passed around will have to be written in that assembly so as that both the server and the client know how to understand each other at this very detailed level.

您需要传递的所有类都必须在该程序集中编写,以便服务器和客户端都知道如何在这个非常详细的级别上相互理解。

If the server needs not understand what is being said between the clients and only pass around messages (broadcasting one message to the other N-1 clients) then forget what I said about the common assembly. In that particular case, the server sees only bytes, while the clients have a deeper understanding of the actual messages being sent back and forth.

如果服务器不需要理解客户端之间的内容,只传递消息(向其他N-1客户端广播一条消息),那么就忘记我对常用程序集的说法了。在该特定情况下,服务器仅看到字节,而客户端对来回发送的实际消息有更深入的了解。

I said I had three approaches.

我说我有三种方法。

The second one involves .NET Remoting which can take a lot of work off your shoulders but which is hard to live with if you don't fully understand it. You can read about it on MSDN, here: http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx

第二个涉及.NET Remoting,它可能需要你的大量工作,但如果你不完全理解它,很难接受。你可以在MSDN上阅读它,在这里:http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx

The third one would be better only if (now, or in the future) by XNA you mean Windows Phone or another implementation of XNA which does not support the BinaryFormatter class (ExEn with MonoTouch, or others). In that case you would have a hard time if you needed your server (a full blown, good old fashioned .NET application) to reference the common assembly I talked about and also have the game project (which would not be a good old fashioned .NET app but have a rather exotic nature) reference the exact same assembly.

第三个会更好,只有当(或现在或将来)XNA你的意思是Windows Phone或XNA的另一个实现,它不支持BinaryFormatter类(ExEn与MonoTouch,或其他)。在这种情况下,如果你需要你的服务器(一个完整的,老式的.NET应用程序)来引用我所谈论的公共程序集并且还有游戏项目(这将不是一个很好的老式),你会很难。 NET应用程序但具有相当奇特的性质)引用完全相同的程序集。

In that case we would need to use and alternate form of serializing and deserializing your objects. You would also need to identically implement two sets of classes in the two worlds (.NET and WP7 or WP8). You could use some form of XML serializers which you would need to map to your classes explicitly (not as powerful as the BinaryFormatter class but more versatile in what the nature of the runtime that host your classes could be).

在这种情况下,我们需要使用和替换形式的序列化和反序列化对象。您还需要在两个世界(.NET和WP7或WP8)中相同地实现两组类。您可以使用某种形式的XML序列化程序,您需要将它们显式映射到您的类(不像BinaryFormatter类那样强大,但在托管类的运行时的性质方面更为通用)。

You can read about the XmlSerializer class on MSDN, here: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

您可以在MSDN上阅读有关XmlSerializer类的信息,请访问:http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

#2


8  

My personal quick-and-clean solution, using JSON.NET:

我的个人快速清洁解决方案,使用JSON.NET:

class JMessage
{
    public Type Type { get; set; }
    public JToken Value { get; set; }

    public static JMessage FromValue<T>(T value)
    {
        return new JMessage { Type = typeof(T), Value = JToken.FromObject(value) };
    }

    public static string Serialize(JMessage message)
    {
        return JToken.FromObject(message).ToString();
    }

    public static JMessage Deserialize(string data)
    {
        return JToken.Parse(data).ToObject<JMessage>();
    }
}

Now you can serialize your objects like so:

现在您可以像这样序列化对象:

Player player = ...;
Enemy enemy = ...;
string data1 = JMessage.Serialize(JMessage.FromValue(player));
string data2 = JMessage.Serialize(JMessage.FromValue(enemy));

Send that data across the wire and then on the other end you can do something like:

通过线路发送数据,然后在另一端,您可以执行以下操作:

string data = ...;
JMessage message = JMessage.Deserialize(data);
if (message.Type == typeof(Player))
{
    Player player = message.Value.ToObject<Player>();
}
else if (message.Type == typeof(Enemy))
{
    Enemy enemy = message.Value.ToObject<Enemy>();
}
//etc...

#3


3  

You could create your own solution using the various classes provided in the .net framework. You would want to checkout WCF or the Sockets namepsace, specifically the TcpClient and TcpListener classes, see MSDN. There are loads of great tutorials if you do a search related to using these. You would also need to consider how to turn your typed objects into byte arrays, similar to this question.

您可以使用.net框架中提供的各种类创建自己的解决方案。您可能想要签出WCF或套接字namepsace,特别是TcpClient和TcpListener类,请参阅MSDN。如果您进行与使用这些相关的搜索,那么有很多很棒的教程。您还需要考虑如何将类型化对象转换为字节数组,类似于这个问题。

An alternative approach would be to use a network library. There are low-level libraries and high-level libraries. Given your level of programming experience and specific end goal I would suggest a high-level library. An example of such a network library would be lidgren. I'm the developer of another network library networkComms.net and a quick example of how you can send typed objects using this library follows:

另一种方法是使用网络库。有低级库和高级库。鉴于您的编程经验水平和特定的最终目标,我建议使用高级库。这种网络库的一个例子就是盖子。我是另一个网络库networkComms.net的开发人员,以及如何使用此库发送类型对象的快速示例如下:

Shared Base (defines Player object):

共享基础(定义播放器对象):

[ProtoContract]
class Player
{
    [ProtoMember(1)]
    public string Name { get; private set; }
    [ProtoMember(2)]
    public int Ammo { get; private set; }
    [ProtoMember(3)]
    public string Position { get; private set; }

    private Player() { }

    public Player(string name, int ammo, string position)
    {
        this.Name = name;
        this.Ammo = ammo;
        this.Position = position;
    }
}

Client (sends a single Player object):

客户端(发送单个Player对象):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using NetworkCommsDotNet;
using ProtoBuf;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Player player = new Player("MarcF", 100, "09.09N,21.12W");

            //Could also use UDPConnection.GetConnection...
            TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000)).SendObject("PlayerData", player);

            Console.WriteLine("Send completed. Press any key to exit client.");
            Console.ReadKey(true);
            NetworkComms.Shutdown();
        }
    }
}

Server:

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using NetworkCommsDotNet;
using ProtoBuf;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            // Convert incoming data to a <Player> object and run this method when an incoming packet is received.
            NetworkComms.AppendGlobalIncomingPacketHandler<Player>("PlayerData", (packetHeader, connection, incomingPlayer) =>
            {
                Console.WriteLine("Received player data. Player name was " + incomingPlayer.Name);
                //Do anything else with the player object here
                //e.g. UpdatePlayerPosition(incomingPlayer);
            });

            //Listen for incoming connections
            TCPConnection.StartListening(true);

            Console.WriteLine("Server ready. Press any key to shutdown server.");
            Console.ReadKey(true);
            NetworkComms.Shutdown();
        }
    }
}

The above is a modified version of this tutorial. You will obviously need to download the NetworkCommsDotNet DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.

以上是本教程的修改版本。您显然需要从网站下载NetworkCommsDotNet DLL,以便您可以在“使用NetworkCommsDotNet”参考中添加它。另请参阅客户端示例中的服务器IP地址当前为“127.0.0.1”,如果您在同一台计算机上同时运行服务器和客户端,这应该可以工作。

#4


1  

After more than 2 years, I've found new ways of solving this issue, and I thought sharing it might be useful to someone. Please note the accepted answer is still valid.

经过两年多的时间,我找到了解决这个问题的新方法,我认为分享它可能对某些人有用。请注意,接受的答案仍然有效。

A simplest way to serialize typed objects I could find is by using the json converter in Json.NET. There's a settings object that allow you to store the type in the json as a value named $type. Here's how to do it and the resulting json:

序列化类型对象的最简单方法是在Json.NET中使用json转换器。有一个设置对象,允许您将类型存储在json中作为名为$ type的值。以下是如何执行此操作以及生成的json:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All
};

JsonConvert.SerializeObject(myObject, settings);

Json result:

Json结果:

{
    "$type" : "Testing.MyType, Testing",
    "ExampleProperty" : "Hello world!"
}

When deserializing, if the same setting is used, an object of the correct type will be deserialized. Exactly what I needed! Hope this helps.

反序列化时,如果使用相同的设置,则将反序列化正确类型的对象。正是我需要的!希望这可以帮助。