博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#网络编程数据传输中封装数据帧头的方法
阅读量:5293 次
发布时间:2019-06-14

本文共 8205 字,大约阅读时间需要 27 分钟。

  在C/S端编程的时候,经常要在C端和S端之间传数据时自定义一下报文的帧头,如果是在C/C++,封装帧头是一件很简单的事情,直接把unsigned char *强转为struct就行,但是在C#中,并没有提供直接从struct到byte[]的转换,这个时候就需要用到Marshal等非托管的方法了。

自定义帧


我们可以在C#中写出如下代码:

1 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1,Size = 12)] 2 [Serializable()] 3  public struct DatagramHeaderFrame 4 { 5             // MessageType类型: 6             public MessageType MsgType; 7  8             //一个四个字节的特征码 9             public uint FeatureCode;10 11             //用于标识报文的长度,用于校验12             public int MessageLength;13   }

  首先我们说明一下,StructLayout是一个用于管理struct的布局特性,

CharSet指示在默认情况下是否应将类中的字符串数据字段作为 LPWSTR 或 LPSTR 进行封送处理;

Pack控制类或结构的数据字段在内存中的对齐方式。

Size指示类或结构的绝对大小。

  LayoutKind是布局的类型,这个枚举有三个值:

Auto运行库自动为非托管内存中的对象的成员选择适当的布局。 使用此枚举成员定义的对象不能在托管代码的外部公开。 尝试这样做将引发异常。

Explicit在未管理内存中的每一个对象成员的精确位置是被显式控制的,服从于 StructLayoutAttribute. Pack 字段的设置。每个成员必须使用 FieldOffsetAttribute 指示该字段在类型中的位置。在MSDN文档中为我们展示了下面的一个例子:

1 [StructLayout(LayoutKind.Explicit)]2 public struct Rect 3 {4    [FieldOffset(0)] public int left;5    [FieldOffset(4)] public int top;6    [FieldOffset(8)] public int right;7    [FieldOffset(12)] public int bottom;8 }

Sequential对象的成员按照它们在被导出到非托管内存时出现的顺序依次布局。 这些成员根据在 StructLayoutAttribute. Pack 中指定的封装进行布局,并且可以是不连续的。

  Serialzable是一个用于指示对象是否能序列化的特性,简单的来说序列化的用处就是,比如我客户端给你传一定的数据,这个数据不是标准的类型的时候,如果我们不使用序列化的时候,我们就要把数据的每个部分都转成二进制然后存储,就很麻烦,而且容易出错,所以C#就提供了这样一个机制给程序员简单使用并且转成二进制(或者其他格式)来使用(使用formatter),而且当一个对象没有被标明为可序列化的时候,我们使用formatter的时候会报错,具体怎么使用请查看即可。比如文档有这样一段代码:

using System;using System.IO;using System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Soap;//using System.Runtime.Serialization.Formatters.Binary;public class Test {   public static void Main()  {      //Creates a new TestSimpleObject object.      TestSimpleObject obj = new TestSimpleObject();      Console.WriteLine("Before serialization the object contains: ");      obj.Print();      //Opens a file and serializes the object into it in binary format.      Stream stream = File.Open("data.xml", FileMode.Create);      SoapFormatter formatter = new SoapFormatter();      //BinaryFormatter formatter = new BinaryFormatter();      formatter.Serialize(stream, obj);      stream.Close();      //Empties obj.      obj = null;      //Opens file "data.xml" and deserializes the object from it.      stream = File.Open("data.xml", FileMode.Open);      formatter = new SoapFormatter();      //formatter = new BinaryFormatter();      obj = (TestSimpleObject)formatter.Deserialize(stream);      stream.Close();      Console.WriteLine("");      Console.WriteLine("After deserialization the object contains: ");      obj.Print();   }}// A test object that needs to be serialized.[Serializable()]        public class TestSimpleObject  {    public int member1;    public string member2;    public string member3;    public double member4;    // A field that is not serialized.    [NonSerialized()] public string member5;     public TestSimpleObject() {        member1 = 11;        member2 = "hello";        member3 = "hello";        member4 = 3.14159265;        member5 = "hello world!";    }    public void Print() {        Console.WriteLine("member1 = '{0}'", member1);        Console.WriteLine("member2 = '{0}'", member2);        Console.WriteLine("member3 = '{0}'", member3);        Console.WriteLine("member4 = '{0}'", member4);        Console.WriteLine("member5 = '{0}'", member5);    }}

 

封装和解析


  要解析一个struct并且把他变成bytes,需要用到非托管的方法:

public static byte[] StructToBytes(object structObj)        {            int size = Marshal.SizeOf(structObj);            IntPtr buffer = Marshal.AllocHGlobal(size);            try            {                Marshal.StructureToPtr(structObj, buffer, false);                byte[] bytes = new byte[size];                Marshal.Copy(buffer, bytes, 0, size);                return bytes;            }            finally            {                Marshal.FreeHGlobal(buffer);            }        }

要把bytes变成sturct,反过来即可:

1         public static object BytesToStruct(byte[] bytes, Type strcutType) 2         { 3             int size = Marshal.SizeOf(strcutType); 4             IntPtr buffer = Marshal.AllocHGlobal(size); 5             try 6             { 7                 Marshal.Copy(bytes, 0, buffer, size); 8                 return Marshal.PtrToStructure(buffer, strcutType); 9             }10             finally11             {12                 Marshal.FreeHGlobal(buffer);13             }14         }

下面演示一下在socket上传输报文+帧头:

1         public static byte[] PackingMessageToBytes 2             (MessageType messageType, uint featureCode, int messageLength, byte[] msgBytes) 3         { 4             DatagramHeaderFrame frame = new DatagramHeaderFrame(); 5             frame.MsgType = messageType; 6             frame.FeatureCode = featureCode; 7             frame.MessageLength = messageLength; 8  9             byte[] header = StructToBytes(frame);10 11             byte[] datagram = new byte[header.Length + msgBytes.Length];12             header.CopyTo(datagram, 0);13             msgBytes.CopyTo(datagram, FrameSize);14 15             return datagram;16         }17 18         /// 19         /// 封装消息和报文20         /// 21         /// 报文帧头22         /// 报文23         /// 编码器24         /// 
25 public static byte[] PackingMessageToBytes26 (DatagramHeaderFrame headerFrame, byte[] msgBytes)27 {28 byte[] header = StructToBytes(headerFrame);29 30 byte[] datagram = new byte[header.Length + msgBytes.Length];31 header.CopyTo(datagram, 0);32 msgBytes.CopyTo(datagram, FrameSize);33 34 return datagram;

 

接收端代码节选:

1 DatagramHeaderFrame headerFrame = new DatagramHeaderFrame();2 headerFrame.MsgType = messageType;3 headerFrame.MessageLength = bytes.Length;4 byte[] datagram = PackingMessageToBytes(headerFrame, bytes);5 6 GetStream().BeginWrite(datagram, 0, datagram.Length, HandleDatagramWritten, this);

 

发送端代码节选:

1 DatagramHeaderFrame headerFrame = new DatagramHeaderFrame(); 2 byte[] datagramBytes = new byte[0]; 3  4 byte[] datagramBuffer = (byte[])ar.AsyncState; 5 byte[] recievedBytes = new byte[numberOfRecievedBytes]; 6  7 Buffer.BlockCopy(datagramBuffer, 0, recievedBytes, 0, numberOfRecievedBytes); 8                 PrasePacking(recievedBytes, numberOfRecievedBytes, ref headerFrame, ref datagramBytes); 9 10 GetStream().BeginRead(datagramBuffer, 0, datagramBuffer.Length, HandleDatagramReceived, datagramBuffer);

 

C++端解析和封装的代码(用Qt写的)

1 QByteArray TcpHeaderFrameHelper::bindHeaderAndDatagram(const TcpHeaderFrame &header,const QByteArray &realDataBytes) 2 { 3     QByteArray byteArray, temp; 4     temp.resize(4); 5      6     unsignedToQByteArray((unsigned)header.messageType, temp); 7     byteArray += temp; 8      9     unsignedToQByteArray((unsigned)header.featureCode, temp);10     byteArray += temp;11     12     unsignedToQByteArray((unsigned)header.messageLength, temp);13     byteArray += temp;14     15     byteArray +=realDataBytes;16     return byteArray;17 }18 19 void TcpHeaderFrameHelper::praseHeaderAndDatagram(const QByteArray &dataBytes,TcpHeaderFrame &headerFrame,QByteArray &realDataBytes)20 {21     realDataBytes.resize(dataBytes.size() - TcpHeaderFrameHelper::headerSize);22     headerFrame.messageType = qByteArrayToInt(dataBytes.left(4));23     headerFrame.featureCode = qByteArrayToInt(dataBytes.mid(4,4));24     headerFrame.messageLength = qByteArrayToInt(dataBytes.mid(8,4));25     26     realDataBytes = dataBytes.mid(12, dataBytes.size());27 }28 29 unsigned TcpHeaderFrameHelper::qByteArrayToInt(QByteArray bytes)30 {31     int result = 0;32     result |= ((bytes[0]) & 0x000000ff); 33     result |= ((bytes[1] << 8) & 0x0000ff00); 34     result |= ((bytes[2] << 16) & 0x00ff0000); 35     result |= ((bytes[3] << 24) & 0xff000000); 36     37     return result;38 }39 40 void TcpHeaderFrameHelper::unsignedToQByteArray(unsigned num, QByteArray &bytes)41 {42     bytes.resize(4);43     bytes[0] = (char)( 0x000000ff & num);44     bytes[1] = (char)((0x0000ff00 & (num)) >> 8);45     bytes[2] = (char)((0x00ff0000 & (num)) >> 16);46     bytes[3] = (char)((0xff000000 & (num)) >> 24);47 }

 

转载于:https://www.cnblogs.com/Philip-Tell-Truth/p/6149539.html

你可能感兴趣的文章
css背景样式
查看>>
JavaScript介绍
查看>>
js中函数与对象的使用
查看>>
正则表达式
查看>>
开源网络漏洞扫描软件
查看>>
yum 命令跳过特定(指定)软件包升级方法
查看>>
创新课程管理系统数据库设计心得
查看>>
Hallo wolrd!
查看>>
16下学期进度条2
查看>>
前端页面卡顿-代码优化
查看>>
nagios通过脚本对系统进行定制监控
查看>>
jquery判断点击鼠标左、中、右键事件
查看>>
java线程池原理
查看>>
为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不能反过来?...
查看>>
Could not resolve view with name '***' in servlet with name 'dispatcher'
查看>>
springBoot配置elasticsearch搜索
查看>>
Chapter 3 Phenomenon——12
查看>>
MyBatis源码解析【6】SqlSession运行
查看>>
中小学教育缴费遇到的一些问题
查看>>
FAIR开源Detectron:整合全部顶尖目标检测算法
查看>>