小弟最近在研究利用Marshal实现序列化和反系列化,主要是使用Marshal类里的两个方法,StructureToPtr和PtrToStructure,这两个类的具体讲解和用法我就不多赘述 了,具体见[url=http://blog.csdn.net/wem520/article/details/9101549][/url],下面说我碰到的问题,我的需求是对一个类的对象通过StructureToPtr序列化成byte[]数组,然后通过TCP Socket传输到另外一个解决方案,再用PtrToStructure反序列化出来这个对象。现在的问题是类的字段类型问题,就是我一个属性是string类型,其余的都是int类型。测试结果发现如果在同一个API里,这个方法能完美实现,但是经过传输再反序列化后int类型可以反序列化出来而string字段就为空或者乱码。小弟百思不得其解,特来求高手帮忙分析解疑。具体代码如下:
API直接序列化反序列化
[code=csharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Person p = new Person();
p.name = "joseph";
p.age = 18;
p.high = 188;
p.kg = 143;
p.tel = 15689;
byte[] bs = StructToBytes(p);
Person2 py2 = (Person2)BytesToStruct(bs, typeof(Person2));
Console.ReadKey();
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class Person
{
public string name { get; set; }
public int age { get; set; }
public int high { get; set; }
public int kg { get; set; }
public int tel { get; set; }
}
//1、struct转换为Byte[]
public static Byte[] StructToBytes(Object structure)
{
Int32 size = Marshal.SizeOf(structure);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structure, buffer, false);
Byte[] bytes = new Byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
//2、Byte[]转换为struct
public static Object BytesToStruct(Byte[] bytes, Type strcutType)
{
Int32 size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, buffer, size);
return Marshal.PtrToStructure(buffer, strcutType);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
}
}
[/code]
这个方案输出结果完全正确!
下面是我的传输Demon
序列化公共类
[code=csharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace Serialization
{
public class Serializable
{
public static Byte[] StructToBytes(Object structure)
{
Int32 size = Marshal.SizeOf(structure);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(structure, buffer, true);
Byte[] bytes = new Byte[size];
Marshal.Copy(buffer, bytes, 0, size);
return bytes;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
//2、Byte[]转换为struct
public static Object BytesToStruct(Byte[] bytes, Type strcutType)
{
Int32 size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, 0, buffer, size);
return Marshal.PtrToStructure(buffer, strcutType);
}
catch (Exception ex)
{
return ex.Message;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public class Person
{
public string name { get; set; }
public int age { get; set; }
public int high { get; set; }
public int kg { get; set; }
public int tel { get; set; }
}
}
}
[/code]
下面是调用,分为客户端和服务器,客户端将内容通过Socket传输给服务器(可以看到我中见的一些注释是在尝试编码格式来解决string类型出错)
Client
[code=csharp]
byte[] Data;
Channel channel;
Serializable.Person p;
private void send_Click(object sender, EventArgs e)
{
p = new Serializable.Person();
//byte[] byt = System.Text.UnicodeEncoding.UTF8.GetBytes(name.Text.ToString());
//p.name = System.Text.UnicodeEncoding.UTF8.GetString(byt);
p.name = name.Text.ToString();
p.age = int.Parse(age.Text);
p.high = int.Parse(high.Text);
p.kg = int.Parse(kg.Text);
p.tel = int.Parse(tel.Text);
Data = Serializable.StructToBytes(p);
if (channel != null && Data != null)
{
channel.Send(Data);
Log("发送成功");
}
else
{
Log("数据体为空或信道连接失败");
return;
}
}
[/code]
Server(我只贴出接受数据的方法)
[code=csharp]
public void DataReceive(object sender, ChannelReceiveArg arg)
{
Log("接收到数据" + arg.Data.ToString());
Serializable.Person p = (Serializable.Person)Serializable.BytesToStruct(arg.Data, typeof(Serializable.Person));
name.Text = p.name;
age.Text = p.age.ToString();
high.Text = p.high.ToString();
kg.Text = p.kg.ToString();
tel.Text = p.tel.ToString();
}
[/code]
这个是我的测试,和API思路一样,只是中间多虑步传输,但就是会有各种问题,有时候会在 return Marshal.PtrToStructure(buffer, strcutType);处报这样的错[img=http://img.bbs.csdn.net/upload/201404/01/1396336717_779070.png][/img],有时候运行正常,但是就是那个name出不来。我真是快疯了,求大神啊!
Int32 size = Marshal.SizeOf(structure);
你为 p.name = "joseph" 设置不同长度的字符串,你看看这里的 size 是多大?
这个以前就验证过,无论设置多长的字符串,size都是20,我认为它把所有的字符串都压缩在头4个字节里头了,或者说这个只是存放了一个地址指针?
@斌斌1989: 只存放了一个地址指针。你的前一个认为太不靠谱了,你要是给放一个 1024 * 1024 字节的字符串,它都给你压缩到 4 个字节里,你认为可能吗?
不好意思,这个你可能看得费劲,我是从csdn上写好直接复制到这里来了,没想到格式不一样,你去这里看会舒服写http://bbs.csdn.net/topics/390748254,谢谢。
@斌斌1989: 问题已经给你找到了,你还让我看啥?
@Launcher: 那也就是说,我存进byte数组里的实际上是个地址指针,当它经过TCP传输后,再反序列化那个地址已经无效了?string实际上不是值引用类型的吗?为什么会出现int存值,而string不能呢?
@Launcher: 针对这个问题有没有解决办法呢?还有你认为这个序列化方式比起protobuf序列化哪个性能会好点呢?
@斌斌1989: 你自己再去翻翻书吧!反正我是没看过书的。不过顺便把 C++ 中 sizeof 也给你普及下,免得你犯同样的错误:http://www.cnblogs.com/wanghetao/archive/2012/04/04/2431760.html
@斌斌1989: 你这个性能肯定差,你自己看看,先 Alloc 了,然后 copy 出来,有你这功夫,直接就用 StreamWritter 直接写了。protobuf 是序列化的协议,跟你这里有区别,你可以用你的什么 PtrToxxxx 来实现 protobuf。
@Launcher: 谢谢你的指点,还有你推荐的sizeof文章,受教了。看来这种方式在跨进程时是不能用了!