问题描述
各位高手:请问在C#中使用Binaryformater将自定义数据结构序列化时,如定义结构structSTData{intnum;chartype;shortarr;doublex;};结构采用单字节对齐方式,序列化后字节的数组比结构实际的字节数要大几倍,如何让序列化后字节数组的大小等于实际结构的字节数?由于需要C#通过UDP与VC6之间进行数据通信,在VC6中结构使用单字节对齐后,转换后的字节数组就是实际结构的字节之和,而如果C#转换后的字节数组大小与VC6的字节数组大小不同就无法确保C#与VC6之间的正常通信。请问如何解决这个问题?
解决方案
解决方案二:
没搞过,帮你顶
解决方案三:
最好自己处理,截取收到的数据包的每个字节,进行相应的转换后给的Struct赋值。别想着偷懒,有时候最笨的办法就是最有效的办法。
解决方案四:
不是序列化数据结构,而是序列化数据。在C#里实现方法:自定义标签,给需要序列化的属性贴上然后用反射的方法读取这个struct或者class,检索贴有此标签的属性,输出到xml流中vc中通过udp传递过去的xml数据流,将数据填充到对应的struct或者class中去
解决方案五:
Binaryformatercouldaddsomemeta-data,sotheserializedresultismorethanthememoryrepresentation.Youmightwanttodothis:[StructLayout(LayoutKind.Sequential,Pack=1)]structSTData{intnum;chartype;shortarr;doublex;};classProgram{staticvoidMain(){STDatadata=newSTData();byte[]buf=newbyte[Marshal.SizeOf(data)];//copySTDataintoabytearray,itiseasierthandirectaccessingapinnedmemoryunsafe{byte*pB=(byte*)&data;for(inti=0;i<buf.Length;i++){buf[i]=pB[i];}}//nowyoumaysendthebufoverUDP}}
解决方案六:
引用4楼gomoku的回复:
Binaryformatercouldaddsomemeta-data,sotheserializedresultismorethanthememoryrepresentation.Youmightwanttodothis:C#code[StructLayout(LayoutKind.Sequential,Pack=1)]structSTData{intnum;chartype;shortarr;doublex;};classProgram{staticvoidMain(){STDatadata=newSTData();byte[]buf=newbyte[M…
定义类似如下的结构和类:[Serializable][StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTCord{publicdoublex;//publicdoubley;publicbytetype;publicintnum;};[Serializable][StructLayout(LayoutKind.Sequential,Pack=1)]publicclassSTPara{publicbytetype1;//publicint[]fre=newint[5];//publicbytetype2;//};[Serializable][StructLayout(LayoutKind.Sequential,Pack=1)]//publicclassSTChar{publicbytenum;//publicdoubleTime;//publicSTCordPos;//publicintPw;//publicSTParaPara;//publicbytetype;//publicSTTargetCharacter(){Para=newSTPara();}};请教专家:按照您所给的方法确实能够实现将自定义结构转换为byte数组,但还有以下两个问题:第一:如果对于自定义类类型如STChar,对其进行强制类型的转换byte*pB=(byte*)&data将报错“无法获取托管类型的地址和大小,或无法声明指向它的指针”,该如何将类变量STChar转换为byte数组呢?第二:由于是C#与VC6之间进行相互通信,C#又该如何将接收到的byte数组转换为对应的结构或类呢?C#中是否有比较简单的办法实现这些转换,而不需要对每一种自定义类型中的每个变量进行单独的转换后再合成为一个byte数组,否则一旦数据类型较多,转换的工作量会非常大,而且某一数据类型有变化,转换方法也必须做出相应的改变。谢谢!
解决方案七:
自己实现序列化接口,或者序列化抽象类。把所有要序列化的类或结构体转为byte数组传递有两种类,一种是序列化后长度固定的类。可以用更快速的内存copy序列化。#region缓存正文索引类///<summary>///缓存正文索引类///</summary>internalsealedclassFileCacheContentIndex{internallongPassTime=0;//过期时间internallongKey;//模板时间刻度internalbyteFileName=0;//文件名internallongIndexStart=0;//本身的开始位置internalbyteNextFileName=0;//下一个文件名internallongNextIndexStart=0;//下一个本身的开始位置//为0的时候关闭不为0开启internallongContentIndexStart=0;//正文开始位置//为0的时候关闭不为0开启internaluintContentIndexLen=0;//正文的长度internalstringUrlKey=null;//访问路径的key带参数的url路径了。(len32)internalconstintClassLength=TempletCache.uintl+TempletCache.bytel*2+TempletCache.longl*5+32;internalbyteIsChange=0;//是否已经被修改了0没有1已经被修改了这个应该是内存变量,不向文件内记录//===============================================//internalFileCacheContentIndexMFont=null;//内存中上一个正文索引类指向internalFileCacheContentIndexMNext=null;//内存中下一个正文索引类指向internalFileCacheContentIndexFont=null;//上一个正文索引类指向internalFileCacheContentIndexNext=null;//下一个正文索引类指向internalFileCacheContentContent=null;//缓存正文类}#endregion
这样对长度不变的类进行序列化和反序列化#region序列化“缓存正文索引类”internalstaticbyte[]SerializableContent(FileCacheContentIndexFC){byte[]BA=newbyte[FileCacheContentIndex.ClassLength];intl=0;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.PassTime),0,BA,l,TempletCache.longl);l+=TempletCache.longl;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.Key),0,BA,l,TempletCache.longl);l+=TempletCache.longl;BA[l++]=FC.FileName;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.IndexStart),0,BA,l,TempletCache.longl);l+=TempletCache.longl;BA[l++]=FC.NextFileName;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.NextIndexStart),0,BA,l,TempletCache.longl);l+=TempletCache.longl;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.ContentIndexStart),0,BA,l,TempletCache.longl);l+=TempletCache.longl;System.Buffer.BlockCopy(System.BitConverter.GetBytes(FC.ContentIndexLen),0,BA,l,TempletCache.uintl);l+=TempletCache.uintl;System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(FC.UrlKey),0,BA,l,32);returnBA;}#endregion#region反序列化“缓存正文索引类”internalstaticFileCacheContentIndexInstanceContent(byte[]BA){FileCacheContentIndexMA=newFileCacheContentIndex();byte[]Bn=newbyte[TempletCache.uintl];byte[]Bnlong=newbyte[TempletCache.longl];intl=0;System.Buffer.BlockCopy(BA,l,Bnlong,0,TempletCache.longl);MA.PassTime=System.BitConverter.ToInt64(Bnlong,0);l+=TempletCache.longl;System.Buffer.BlockCopy(BA,l,Bnlong,0,TempletCache.longl);MA.Key=System.BitConverter.ToInt64(Bnlong,0);l+=TempletCache.longl;MA.FileName=BA[l++];System.Buffer.BlockCopy(BA,l,Bnlong,0,TempletCache.longl);MA.IndexStart=System.BitConverter.ToInt64(Bnlong,0);l+=TempletCache.uintl;MA.NextFileName=BA[l++];System.Buffer.BlockCopy(BA,l,Bnlong,0,TempletCache.longl);MA.NextIndexStart=System.BitConverter.ToInt64(Bnlong,0);l+=TempletCache.uintl;System.Buffer.BlockCopy(BA,l,Bnlong,0,TempletCache.longl);MA.ContentIndexStart=System.BitConverter.ToInt64(Bnlong,0);l+=TempletCache.uintl;System.Buffer.BlockCopy(BA,l,Bn,0,TempletCache.uintl);MA.ContentIndexLen=System.BitConverter.ToUInt32(Bn,0);l+=TempletCache.uintl;byte[]Ba1=newbyte[32];System.Buffer.BlockCopy(BA,l,Ba1,0,32);MA.UrlKey=System.Text.Encoding.ASCII.GetString(Ba1);returnMA;}#endregion
序列化后长度不固定的类。就只能实现序列化接口了#region序列化基础抽象类internalabstractclassSerializableAbstractClass{internalabstractvoidLoad(BinaryReaderrdr);internalabstractvoidSave(BinaryWriterwrt);}#endregion#region缓存正文类///<summary>///缓存正文类///</summary>internalsealedclassFileCacheContent:SerializableAbstractClass{internalbyteIsArray=0;//是否有局部缓存1全局2局部internalushortCount=0;//数组的长度internalstringContent=null;//全局正文缓存//局部的时候位空internalstring[]ConArray=null;//局部缓存#region重载实现读函数///<summary>///重载实现读函数///</summary>///<paramname="rdr"></param>internaloverridevoidLoad(BinaryReaderrdr){IsArray=rdr.ReadByte();Count=rdr.ReadUInt16();if(IsArray==1){Content=rdr.ReadString();}else{ConArray=newstring[Count];for(inti=0;i<Count;i++){ConArray[i]=rdr.ReadString();}}}#endregion#region重载实现写函数///<summary>///重载实现写函数///</summary>///<paramname="wrt"></param>internaloverridevoidSave(BinaryWriterwrt){wrt.Write(IsArray);wrt.Write(Count);if(IsArray==1){wrt.Write(Content);}else{for(inti=0;i<ConArray.Length;i++){wrt.Write(ConArray[i]);}}}#endregion}#endregion
这样序列化和反序列化#region变长数据函数internalstaticbyte[]WriteData(SerializableAbstractClassSI){byte[]BA=null;MemoryStreamfs=null;BinaryWriterwrt=null;try{fs=newMemoryStream(500);wrt=newBinaryWriter(fs);SI.Save(wrt);BA=fs.ToArray();returnBA;}catch{returnnewbyte[0];}finally{wrt.Close();fs.Close();}}#endregion#region读数组internalstaticboolReadData(SerializableAbstractClassSI,byte[]BA){MemoryStreamfs=null;BinaryReaderread=null;try{fs=newMemoryStream(BA);read=newBinaryReader(fs);SI.Load(read);returntrue;}catch{returnfalse;}finally{read.Close();fs.Close();}}#endregion
解决方案八:
其实也可以很简单的用结构处理,只是要记得通知数组的大小:classProgram{[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTCord{publicdoublex;publicdoubley;publicbytetype;publicintnum;};[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTPara{publicbytetype1;[MarshalAs(UnmanagedType.ByValArray,SizeConst=5)]//<-----publicint[]fre;publicbytetype2;};[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTChar{publicbytenum;publicdoubleTime;publicSTCordPos;publicintPw;publicSTParaPara;publicbytetype;};staticvoidMain(string[]args){intsize=Marshal.SizeOf(typeof(STChar));//size==57}}
解决方案九:
如果是使用.net二进制序列化,本身就应该由.net进行反序列化,因为这个序列化是.net的标准。如果你是跨语言的序列化建议采用xml序列化形式,例如与java通讯,甚至和VC++,因为这样的序列化对象比较不是简单的内存数据传导,而是一个对象的反序列化实例,即使它是值类型。如果你确实想用二进制的序列化和其他语言的通讯,可以自己完成序列化的过程,但是这个我认为没有比较转换为非托管结构。
解决方案十:
对于结构可以这样处理,对于类变量该如何处理呢?另外C#又如何将接收到的byte数组转换为对应的结构或类呢?感谢yanqing5266的帮助,你的方法还没有试,不知道转换后的字节是否与VC6的memcpy命令转换后的字节数相同,不然不能实现C#与VC6之间的相互通信。谢谢zhujiechang,XML序列化也增加了无数类容,无法用VC6去正确解包,并且影响通信速度。
解决方案十一:
引用7楼gomoku的回复:
其实也可以很简单的用结构处理,只是要记得通知数组的大小:C#codeclassProgram{[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTCord{publicdoublex;publicdoubley;publicbytetype;publicintnum;};[StructLayout(LayoutKind.Sequential,Pack=1)]publicstructSTPara{…
对于结构可以这样处理,对于类变量该如何处理呢?另外C#又如何将接收到的byte数组转换为对应的结构或类呢?感谢yanqing5266的帮助,你的方法还没有试,不知道转换后的字节是否与VC6的memcpy命令转换后的字节数相同,不然不能实现C#与VC6之间的相互通信。谢谢zhujiechang,XML序列化也增加了无数类容,无法用VC6去正确解包,并且影响通信速度。