我在c#中的客户端/服务器编程中做了一些阅读。我对这个过程非常熟悉,可以提出以下问题:
如何通过tcp / ip而不仅仅是字符串传输结构对象?
我的应用是一款具有聊天功能的联网游戏。所以我不想只传输文本,而是想要构建一个包含两个字段的数据结构或类结构:i。包类型ii。数据包类型的数据
我会在应用程序执行期间需要时传输它,并在接收端解码数据对象并将其放在它所属的位置。
我不寻找代码,只是一些想法和搜索语句,我可以提供给谷歌,所以我会;有更好的理解。
我读过有关序列化/反序列化的内容,是他的出路吗?
感谢。
我查看过相关主题的帖子,但仍希望得到进一步的指导。
答案 0 :(得分:7)
最终是的:你在谈论序列化。这可以采用多种形式,特别是在.NET中,但最终需要在:
之间进行选择My.Namespace.FooBar
实例”。这使得它很容易上班,但很少在不同平台之间工作(通常不在版本之间) - 所有类型信息都可以是详细的手动序列化方法包括(仅提及“序列化程序”关键字):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的简单套接字处理示例是在其中一个示例项目中(here, in fact)
答案 1 :(得分:6)
如果您不需要丰富的序列化 - 如果您只想将结构写入字节数组,请考虑Marshal类。
例如,考虑使用C#中的tar应用程序。 tar格式基于512字节块,并且系列中的第一个块具有规则结构。理想情况下,应用程序只需要blitt磁盘文件中的数据,直接进入结构。 Marshal.PtrToStructure方法执行此操作。这是结构。
[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.
然后这是一个执行blitting的泛型类。
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;
}
}
您可以将该类与任何结构一起使用。您必须使用LayoutKind.Sequential并将自己限制为blittable类型(基本上是基元和相同的数组)才能使用此方法。它在代码,性能和内存方面快速而有效,但它在如何使用方面受到一定限制。
获得字节数组后,可以通过NetworkStream等传输它,然后在另一端使用相同的类进行反序列化。
答案 2 :(得分:5)
序列化是最简单的方法,因为系统直接支持它。但是,对于大而复杂的对象存在一些性能问题。在你的情况下,听起来像序列化是要走的路。如果您想要更低级别的东西,您可以查看BinaryWriter / BinaryReader,它允许您自己完成工作。
答案 3 :(得分:3)
您可以基于Socket创建NetworkStream,并使用任何Stream机制来传输数据。这会将您的问题转换为:如何从/向Stream读取/写入结构。
您可以使用序列化,也可以使用BinaryWriter / BinaryReader。对于一个小结构(就像你描述的那样),我会写一些自定义方法:
var netStream = new NetworkStream(clientSocket, true);
var writer = new BinaryWriter(netStream);
writer.Write(data.Value1);
writer.Write(data.Value2);
对于较大的结构,我会考虑Cheeso的Marshaling选项。
答案 4 :(得分:1)
.NET的二进制序列化可能是最快的开箱即用选项,假设通信机制的两端都是C#并且可以加载包含消息类型的相同程序集。如果您的结构非常简单,那么滚动您自己的序列化可能就好了。只需在类中定义数据结构,也可以在字符串中定义数据结构。
答案 5 :(得分:1)
使用对象序列化,你在正确的轨道上。
我想到的一件事我还没有提到过,二进制序列化器通常会创建一个较少的字节来通过套接字发送,但是如果您使用XML或JSON序列化程序然后使用CompressionStream压缩结果(GZipStream?)在通过网络流发送之前,您可能会根据对象中的数据类型获得更小的尺寸(当您有大量字符串时,这种方法效果最佳)。
这将需要更多的CPU时间来发送和读取消息,因此如果您需要降低带宽要求,则需要进行权衡。