通过TCP或套接字发送类型化的对象 [英] Send typed objects through tcp or sockets

查看:73
本文介绍了通过TCP或套接字发送类型化的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法为在Xna中制作的一款非常简单的游戏创建网络界面.我只需要通过TCP客户端/套接字发送对象.前任:我有一个名为"Player"的类.在每个播放器中,都有一个字段名称"Info",类型为"PlayerInfo".在客户端/服务器中,我需要将每个玩家的信息发送给每个客户端(发送者除外)(显然).这是一个简单的示例,但我需要使用大约5 – 10个对象,并向玩家发送更新(位置,动作等).有没有一种简单的方法可以使用TCP/Sock做到这一点?注意:我对C#和编程的了解为6/10,因此,如果您有解决方案,则无需解释所有内容(例如:变量和字段之间的区别是什么).我也了解接口,库等...预先感谢!

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!

推荐答案

我会推荐一种方法,而第二种方法则取决于很多事情.

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

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

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.

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

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

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

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

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).

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

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.

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

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:

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);
  }

}

BinaryFormatter类能够从作为Serialize(Stream,object)方法的第二个参数提供的根/标记开始遍历对象的树/图,并写入所有原始值以及类型信息和相对位置信息到提供的流.只要提供的流的位置与先前对象图序列化的位置相对应,它也可以进行完全相反的操作并反序列化整个对象图.

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.

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

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]
public class Player {
  public PlayerInfo Info; 
  //... etc 

然后,您还需要使用[SerializableAttribute]进行注释:

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

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

如果您的类包含其他人(例如Microsoft)编写的类型的字段,则最好已经使用属性对其进行了注释.大多数可以序列化的文件已经存在.基本类型自然是可序列化的.不应序列化的内容包括:文件流,线程,套接字等.

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.

在确保拥有可序列化的类之后,您只需对它们的实例进行序列化,发送,接收并反序列化即可:

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

}

在服务器端:

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.

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

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.

我说过我有三种方法.

第二个涉及.NET Remoting,它可以让您省去很多工作,但如果您不完全了解它,则很难忍受.您可以在以下MSDN上阅读有关它的信息: http://msdn.microsoft.com/en-us/library/kwdt6w2k(v = vs.100).aspx

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

仅当(现在或将来)使用XNA表示Windows Phone或不支持BinaryFormatter类的XNA的另一种实现(带有MonoTouch的ExEn或其他)时,第三种更好.在这种情况下,如果您需要服务器(功能完善的老式.NET应用程序)来引用我所讨论的通用程序集,并且还拥有游戏项目(这不是老式的,那么您将很难).NET应用程序,但具有相当奇特的性质)引用完全相同的程序集.

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.

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

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).

您可以在此处了解有关MSDN上XmlSerializer类的信息: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx

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

这篇关于通过TCP或套接字发送类型化的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆