客户端采用C++开发,服务端采用C#开发,所以双方必须保证各自定义结构体成员类型和长度一致才能保证报文解析的正确性.

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = )]
public struct Head
{
public ushort proMagic; //包起始标记:固定0x7e7e
public ushort proPackLen; //包长度:包头 + 数据区 + 包尾长度,注意不要超过最大长度限制
public long proSrcAddr; //源地址:不使用,填0
public ushort proSrcPort; //源地址端口:不使用,填0
public long proDstAddr; //目的地址:不使用,填0
public ushort proDstPort; //目的端口:不使用,填0
public ushort proCmdCode; //命令码:参见以上命令码定义 public ushort proVersion; //版本号:不使用,填1
public char proSerial; //报文序号:一条报文实例对应一个序号,不同报文叠加,0-255往复
public ushort proPackSum; //总包数:当包长超过最大长度限制时,需要拆包,大包拆小包总数,不拆默认1
public ushort proPackId; //当前包号:对应以上总包数的小包标识,不拆默认0 }

一、首先是 [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)],这是C#引用非托管的C/C++的DLL的一种定义定义结构体的方式,主要是为了内存中排序,LayoutKind有两个属性Sequential和Explicit,Sequential表示顺序存储,结构体内数据在内存中都是顺序存放的,CharSet=CharSet.Ansi表示编码方式。这都是为了使用非托管的指针准备的,这两点大家记住就可以。

       需要注意的是 Pack = 1 这个特性,它代表了结构体的字节对齐方式,在实际开发中,C++开发环境开始默认是2字节对齐方式 ,拿上面报文包头结构体为例,char类型在虽然在内存中至占用一个字节,但在结构体转为字节数组时,系统会自动补齐两个字节,所以如果C#这面定义为Pack=1,C++默认为2字节对齐的话,双方结构体会出现长度不一致的情况,相互转换时必然会发生错位,所以需要大家都默认1字节对齐的方式,C#定义Pack=1,C++ 添加 #pragma pack 1,保证结构体中字节对齐方式一致。

二、数组的定义,结构体中每个成员的长度都是需要明确的,因为内存需要根据这个分配空间,而C#结构体中数组是无法进行初始化的,这里我们需要在成员声明时进行定义;

    /// <summary>
/// 终端信息查询
/// </summary>
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct PackTerminalSearch5001
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
/// <summary>
/// 终端编号
/// </summary>
public string stationCode; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
/// <summary>
/// 回复指令
/// </summary>
public Byte[] order;
}
/// <summary>
/// 终端信息数据
/// </summary> [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct PackTerminalSearch3004
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
/// <summary>
/// 终端编号
/// </summary>
public string stationCode;
/// <summary>
/// 终端IP
/// </summary>
public long terminalIP;
/// <summary>
/// 终端端口
/// </summary>
public ushort terminalPort;
/// <summary>
/// 中心IP
/// </summary>
public long serverIP;
/// <summary>
/// 测站端口
/// </summary>
public ushort serverPort;
/// <summary>
/// 磁盘信息数组
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public PackDiskInfo[] diskInfoArray;
} /// <summary>
/// 磁盘信息
/// </summary>
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct PackDiskInfo
{
/// <summary>
/// 盘符
/// </summary>
public char drive;
/// <summary>
/// 总空间
/// </summary>
public double totalSize;
/// <summary>
/// 可用空间
/// </summary>
public double usableSize;
}

上面的代码需要注意的是string类型实际为Char[6]长度的数组,实际使用中只能有效的使用前5个字符,因为char[6]最后一位默认\0;

三、结构体与字节数组的互转

  
        PackTerminalSearch5001 info;
info.stationCode = "12345";
info.order = new byte[6] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
Byte[] recv = StructToBytes(info); object obj = BytesToStuct(recv, typeof(PackTerminalSearch5001));
PackTerminalSearch5001 info5001 = (PackTerminalSearch5001)obj;
byte[] order = info5001.order;

        //// <summary>
/// 结构体转byte数组
/// </summary>
/// <param name="structObj">要转换的结构体</param>
/// <returns>转换后的byte数组</returns>
public static byte[] StructToBytes(object structObj)
{
//得到结构体的大小
int size = Marshal.SizeOf(structObj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(structObj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
} /// <summary>
/// byte数组转结构体
/// </summary>
/// <param name="bytes">byte数组</param>
/// <param name="type">结构体类型</param>
/// <returns>转换后的结构体</returns>
public static object BytesToStuct(byte[] bytes, Type type)
{
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes, 0, structPtr, size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, type);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return obj;
}

C# 结构体定义 转换字节数组 z的更多相关文章

  1. C#中结构体定义并转换字节数组

    最近的项目在做socket通信报文解析的时候,用到了结构体与字节数组的转换:由于客户端采用C++开发,服务端采用C#开发,所以双方必须保证各自定义结构体成员类型和长度一致才能保证报文解析的正确性,这一 ...

  2. 将c语言的结构体定义变成对应的golang语言的结构体定义,并将golang语言结构体变量的指针传递给c语言,cast C struct to Go struct

    https://groups.google.com/forum/#!topic/golang-nuts/JkvR4dQy9t4 https://golang.org/misc/cgo/gmp/gmp. ...

  3. (原创)结构体自动化转为char数组的实现

    结构体自动化转换为char数组这个需求,来自于一个最近开发的一个项目,在项目开发过程中遇到一个小问题,需要将各种结构体拷贝到char数组中,这对于一个简单的结构体来说是很简单的事情,比如下面这个只有整 ...

  4. JNA结构体参数传递,Java数组

    JNA以结构体数组为参数进行调用: ////// C++ // student 结构体定义 typedef struct { int age; char name[20]; }Student; //  ...

  5. ARM单片机的头文件如何用结构体定义地址

    下面我们以ARM Cortex-M0内核单片机LPC1114的头文件lpc11xx.h文件进行说明. 1.先说两句 lpc11xx.h文件是lpc11xx系列单片机包含的头文件.这个文件的作用和51单 ...

  6. Swift类和结构体定义-备

    Swift中的类和结构体定义的语法是非常相似的.类使用class关键词定义类,使用struct关键词定义结构体,它们的语法格式如下: class 类名 { 定义类的成员 } struct 结构体名 { ...

  7. C语言结构体定义的几种方法

    什么是结构体? 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类.结构体可以被声明为变量.指针或数组等,用以实现较复杂的数据 ...

  8. #pragma pack 在BITMAP结构体定义中的使用

    BITMAP位图文件主要分为如下3个部分: 块名称 对应Windows结构体定义 大小(Byte) 文件信息头 BITMAPFILEHEADER 14 位图信息头 BITMAPINFOHEADER 4 ...

  9. 读陈浩的《C语言结构体里的成员数组和指针》总结,零长度数组

    原文链接:C语言结构体里的成员数组和指针 复制例如以下: 单看这文章的标题,你可能会认为好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Lar ...

随机推荐

  1. hdu 3951 硬币围成一圈(博弈)

    n个硬币围成一个环 每次只能取1-K个硬币 最后取完者胜 假如5个硬币 每次取1-2个情况1 先手取1个 后手取剩下4个中间2个 破坏了连续 虽然最后剩2个,但先手只能取一个 然后后再取一个 后手胜 ...

  2. android拾遗——四大基本组件介绍与生命周期

    Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...

  3. oplog扩容

    Oplog的扩容: 背景:一个由3个节点组成的复制集. 主节点:A 从节点:B,C 需求:Oplog扩容,尽量少的影响业务. 思路:先由从节点开始,一台一台的从复制集中剥离,修改,再回归复制集,最后操 ...

  4. USACO 4.4 Pollutant Control (网络流求最小割割集)

    Pollutant ControlHal Burch It's your first day in Quality Control at Merry Milk Makers, and already ...

  5. 5.5版本以上”No input file specified“问题解决

    .htaccess文件中的 RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 在默认情况下会导致No input file specified. 修改成 Rewri ...

  6. PHP的单引号和双引号

    单引号内部的变量不会执行,双引号会执行. <?php $name = 'hello php'; echo "<h1>$name</h1>"; echo ...

  7. linux 杂类

    1.linux 下替换windows换行符命令   set ff=unix(命令行)

  8. 【记录】url 中出现特殊字符该怎么办

    url中出现特殊字符+ URL 中+号表示空格 %2B 空格 URL中的空格可以用+号或者编码 %20/ 分隔目录和子目录 %2F ? 分隔实际的URL和参数 %3F % 指定特殊字符 %25 # 表 ...

  9. <泛> 多路快排

    今天写一个多路快排函数模板,与STL容器兼容的. 我们默认为升序排序 因为,STL容器均为逾尾容器,所以我们这里采用的参数也是逾尾的参数 一.二路快排 基本思路 给你一个序列,先选择一个数作为基数,我 ...

  10. openstack policy 鉴权过程分析

    转:http://blog.chinaunix.net/uid-20940095-id-4144300.html 1. openstack 鉴权简单介绍       众所周知,openstack通过k ...