如何通过TCP/IP传输结构体对象而不仅仅是字符串?
我的应用程序是一个带有聊天功能的网络游戏。因此,我想使用一个数据结构或类结构来传输两个字段:i.数据包类型 ii.数据包类型的数据。
在应用程序执行期间需要时,我会传输这些数据,并在接收端解码数据对象并将其放置在相应位置。
我不是在寻找代码,只是想要一些想法和搜索语句,以便我能更好地理解。
我已经阅读了有关序列化/反序列化的文章,那是正确的方法吗?
谢谢。
我已经查看了相关主题的帖子,但仍然需要进一步的指导。
My.Namespace.FooBar
实例”)。这使得它真正容易使用,但很少在不同平台之间起作用(通常也不在不同版本之间起作用),并且所有这些类型信息可能非常冗长。
- 手动 vs 自动;事实证明:手动序列化程序通常在带宽方面效率最高,因为您可以手动自定义流 - 但需要大量的工作量,并且您需要了解序列化程序。自动序列化程序更适合通用用途(实际上:大多数情况下)。除非别无选择,否则避免手动序列化程序。自动序列化程序处理有关不同类型数据等复杂性。TextWriter
、XmlWriter
、IXmlSerializable
、BinaryWriter
、ISerializable
。你不想这样做... | Contract | Metadata
===============+========================+===========================
Text | XmlSerializer | SoapFormatter
| DataContractSerializer | NetDataContractSerializer
| Json.NET |
---------------+------------------------+---------------------------
Binary | protobuf-net | BinaryFormatter
如果你在谈论原始流,我更喜欢基于二进制协议的序列化器,但是,我写了protobuf-net,所以我可能有偏见 ;-p
与常见的RPC堆栈进行比较:
BinaryFormatter
XmlSerializer
DataContractSerializer
或NetDataContractSerializer
,有时也会使用XmlSerializer
(它还可以配置为使用例如protobuf-net)我可以很高兴地编写一个使用protobuf-net在流上表示不同类型消息的示例,但是一个简单的使用protobuf-net处理套接字的示例在一个示例项目中(实际上在这里)
[StructLayout(LayoutKind.Sequential, Size=512)]
internal struct HeaderBlock
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public byte[] name; // name of file.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] mode; // file mode
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] uid; // owner user ID
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] gid; // owner group ID
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] size; // length of file in bytes
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
public byte[] mtime; // modify time of file
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] chksum; // checksum for header
// ... more like that... up to 512 bytes.
internal class RawSerializer<T>
{
public T RawDeserialize( byte[] rawData )
{
return RawDeserialize( rawData , 0 );
}
public T RawDeserialize( byte[] rawData , int position )
{
int rawsize = Marshal.SizeOf( typeof(T) );
if( rawsize > rawData.Length )
return default(T);
IntPtr buffer = Marshal.AllocHGlobal( rawsize );
Marshal.Copy( rawData, position, buffer, rawsize );
T obj = (T) Marshal.PtrToStructure( buffer, typeof(T) );
Marshal.FreeHGlobal( buffer );
return obj;
}
public byte[] RawSerialize( T item )
{
int rawSize = Marshal.SizeOf( typeof(T) );
IntPtr buffer = Marshal.AllocHGlobal( rawSize );
Marshal.StructureToPtr( item, buffer, false );
byte[] rawData = new byte[ rawSize ];
Marshal.Copy( buffer, rawData, 0, rawSize );
Marshal.FreeHGlobal( buffer );
return rawData;
}
}
您可以基于Socket创建NetworkStream,并使用任何流机制来传输数据。这将转化为如何从/向流中读取/写入结构的问题。
您可以使用Serialization,也可以使用BinaryWriter/BinaryReader。对于小型结构(如您所描述),我会编写一些自定义方法:
var netStream = new NetworkStream(clientSocket, true);
var writer = new BinaryWriter(netStream);
writer.Write(data.Value1);
writer.Write(data.Value2);
序列化是最简单的方法,因为系统直接支持它。然而,对于大型和复杂对象存在一些性能问题。在您的情况下,似乎序列化是最好的选择。如果您想要更低级别的操作,可以尝试使用BinaryWriter/BinaryReader,这允许您自己完成工作。
如果通信机制的两端都是C#并且可以加载包含消息类型的相同程序集,则.NET的二进制序列化可能是最快的开箱即用选项。如果您的结构非常简单,那么自己编写序列化也可能是可以的。只需在类中定义数据结构以及将其转换为字符串和从字符串转换回来的方法。
使用对象序列化是正确的方法。
有一件事情我认为还没有被提到,那就是二进制序列化器通常会创建较少的字节以发送到套接字,但是如果您使用 XML 或 JSON 序列化器,然后在将其发送到网络流之前使用 CompressionStream(GZipStream?)压缩结果,则根据对象中数据类型的不同,您可能会获得更小的大小(当您有大量字符串时,这种方法效果最佳)。
这将需要更多的 CPU 时间来发送和读取消息,因此如果您需要降低带宽要求,则需要进行权衡。
XML序列化是否适合通过防火墙? - iTEgg