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

Phi*_*aré 12 c# sockets tcpclient

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

Edu*_*tru 32

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

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

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

public class MyMessage {
  public byte[] Data { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

你最常用的方法是使用标准库类: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);
  }

}
Run Code Online (Sandbox Code Playgroud)

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

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

[SerializableAttribute]
public class Player {
  public PlayerInfo Info; 
  //... etc 
Run Code Online (Sandbox Code Playgroud)

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

[SerializableAttribute]
public class PlayerInfo { //... etc
Run Code Online (Sandbox Code Playgroud)

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

在服务器端:

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 );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

我说我有三种方法.

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

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

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

您可以在这里阅读MSDN上的XmlSerializer类:http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

  • @CarterNolan:我会尽量保持这个简短.首先,我不明白两个级别的序列化是什么:一个对象本身..好吧,但那么为什么第二个具有对象的XML.我假设您选择XML作为序列化的一种形式.在那种情况下,"对象本身"是什么意思?你知道你实际上不能**通过套接字发送对象**,只是**字节**,序列化的艺术是将**对象**转换为**字节**(反之亦然) ...而且,你也没有发送XML.将XML转换为字节更容易...... (2认同)
  • ......那是因为XML有一个线性表示(你可以立即决定它的长度是什么,或者导航到它的第N个元素,即一个角色,而每个元素本身并没有进一步的神秘感),而对象的图形则是复杂的数学结构.现在,关于你的问题,我可以很快说,因为没有完美的食谱可以在任何地方使用,而且由于我无法理解你在1条评论中的确切情况,我建议:做吧!没关系.这听起来不太糟糕,每个实验都很有用:)我还建议你写一个问题,彻底描述你的问题 (2认同)

Tim*_*lds 10

我的个人快速清洁解决方案,使用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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Player player = ...;
Enemy enemy = ...;
string data1 = JMessage.Serialize(JMessage.FromValue(player));
string data2 = JMessage.Serialize(JMessage.FromValue(enemy));
Run Code Online (Sandbox Code Playgroud)

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

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...
Run Code Online (Sandbox Code Playgroud)

  • 我概述的方法完全解决了您的问题,即如何确定类型.看看我在例子中怎么说`message.Type == typeof(Player)`或`message.Type == typeof(Enemy)`?这就是你"检索演员表的类型"的方法. (2认同)