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

ref: https://www.cnblogs.com/dafanjoy/p/7818126.html

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

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

       首先是结构体定义,一些基本的数据类型,C#与C++都是可以匹配的:

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

 

 

 

转载于:https://www.cnblogs.com/carl2380/p/9900609.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解析robots.txt

案例&#xff1a; http://www.taobao.com/robots.txt 学习&#xff1a; User-agent: * 这里的*代表的所有的搜索引擎种类&#xff0c;*是一个通配符Disallow: /admin/ 这里定义是禁止爬寻admin目录下面的目录Disallow: /require/ 这里定义是禁止爬寻require目录下面的目录Disal…

2018移动端页面适配-自适应最新方案直接写px--------通过gulp工作流搭建一体化的移动端开发环境

1.开始 在flexible的GitHub上面写着 由于viewport单位得到众多浏览器的兼容&#xff0c;lib-flexible这个过渡方案已经可以放弃使用&#xff0c;不管是现在的版本还是以前的版本&#xff0c;都存有一定的问题。建议大家开始使用viewport来替代此方案。vw的兼容方案可以参阅《如…

jclouds的命令行界面

序幕 我使用和为jclouds贡献了一年多的时间。 到目前为止&#xff0c;我已经在很多领域广泛使用了它&#xff0c;尤其是在Fuse生态系统中 。 它的强大之处在于它缺少一件事&#xff0c;该工具可用于管理jclouds也提供访问权限的任何云提供商。 类似于EC2命令之类的工具&#xf…

中兴linux下载软件,国产操作系统中兴新支点使用WPS For Linux办公软件的体验报告...

以下将给你带来在国产操作系统中兴新支点操作系统下使用WPS For Linux办公软件的体验报告&#xff0c;WPS For Linux提供Deb、Rpm、Tar.xz、Snap软件包&#xff0c;你可以选择Tar.xz源码包编译安装&#xff0c;或在系统自带的软件中心下安装&#xff0c;也可以参考采用snap方式…

Java 教程(开发环境配置+基础语法)

Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境。 window系统安装java 下载JDK 首先我们需要下载java开发工具包JDK&#xff0c;下载地址&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/index.html&#xff0c;点击如下下载按钮&am…

数据采集工具Telegraf:简介及安装

接着上一篇博客&#xff1a;InfluxDB简介及安装&#xff0c;这篇博客介绍下Linux环境下Telegraf安装以及其功能特点。。。 官网地址&#xff1a;influxdata 官方文档&#xff1a;telegraf文档 环境&#xff1a;CentOS7.4 64位 Telegraf版本&#xff1a;0.11.1-1 一、Telegraf介…

初探小程序插件

插播公司招聘信息&#xff1a; https://cnodejs.org/topic/5a915706653c43b914684f90 小程序插件可以干嘛&#xff1f; 周二晚上&#xff08;3.13&#xff09;的一个小程序新功能发布了-【小程序插件】&#xff0c;一开始以为是小程序发布了类似npm的组件管理工具&#xff0c;…

流畅和稳定的API的Lambda

几周前&#xff0c;我写了关于Java 8 lambda的介绍 。 在本简介中&#xff0c;我解释了什么是lambda以及如何将它们与Java 8中也引入的新Stream API结合使用。 Stream API为集合提供了更实用的接口。 此接口在很大程度上取决于lambda。 但是&#xff0c;lambda不仅具有改进的收…

linux 内存使用原理,linux中内存使用原理

首先介绍一下linux中内存是如何使用的。当有应用需要读写磁盘数据时&#xff0c;由系统把相关数据从磁盘读取到内存&#xff0c;如果物理内存不够&#xff0c;则把内存中的部分数据导入到磁盘&#xff0c;从而把磁盘的部分空间当作虚拟内存来使用&#xff0c;也称为Swap。如果给…

Confluence 6 站点备份和恢复

Atlassian 推荐针对生产环境中安装使用的 Confluence 使用原始数据库工具备份策略。 在默认的情况下&#xff0c;Confluence 每天都会备份所有数据和附件到 XML 文件备份中。这些文件被称为 XML 站点备份&#xff0c;同时这些文件存储在 Confluence home 目录中的 backups 目录…

休眠事实:等于和HashCode

每个Java对象都继承了equals和hashCode方法&#xff0c;但它们仅对Value对象有用&#xff0c;对面向无状态行为的对象毫无用处。 尽管使用“ ”运算符比较引用很简单&#xff0c;但是对于对象相等而言&#xff0c;事情要复杂一些。 由于您负责告诉平等性对特定对象类型的含义…

从mysql向HBase+Phoenix迁移数据的心得总结

* 转载请注明出处 - yosql473 - 格物致知&#xff0c;经世致用 mysql -> HBase Phoenix 1.总体方案有哪些&#xff1f; 1&#xff09;通过Sqoop直接从服务器(JDBC方式)抽取数据到HBase中 因为数据量非常大&#xff0c;因此优先考虑用Sqoop和MR抽取。 使用Sqoop抽取数据有一…

玩转异步 JS :async/await 简明教程(附视频下载)

课程介绍 在软件开发领域&#xff0c;简洁的代码 > 容易阅读的代码 > 容易维护的代码&#xff0c;而 ES2017 中的 async/await 特性能让我们编写出相比回调地狱和 Promise 链式调用更直观、更容易理解的代码&#xff0c;await 关键字接收一个 Promise&#xff0c;等待代码…

linux 无法找到函数定义,找到定义Linux函数的位置

使用手册页对于基本的C函数&#xff0c;该手册页应该工作。man 2 readman 3 printf第2节为系统调用(直接到内核)&#xff0c;而第3是用于标准C库调用。您通常可以省略该部分&#xff0c;并且人将自己弄清楚您需要什么。请注意&#xff0c;您可能需要采取额外步骤在系统上获取与…

序列自动机—— [FJOI2016]所有公共子序列问题

序列自动机&#xff1a; 是一个处理子序列的自动机。就这样。 建造&#xff1a;&#xff08;By猫老师&#xff1a;immoralCO猫&#xff09; s[] next[][26] memset(next[n], -1, 26<<2); for(int i n; i; --i) {memcpy(next[i - 1], next[i], 26 << 2);next[i - 1…

1000种对Java的响应没有死

当一篇评论发表1000条评论时&#xff0c;值得考虑一下。 我上周的社论“ 如果Java即将死&#xff0c;它肯定看起来非常健康 ”在各个开发人员社区中都感到不安 。 在Reddit&#xff0c;Hacker News和Slashdot之间&#xff0c;它收到了1000多个评论。 奇怪的是&#xff0c;很少…

java导包

下载响应的zip文件&#xff0c;就可以导入了&#xff0c;导入src目录也是可以的。 转载于:https://www.cnblogs.com/liaoxiaolao/p/9902062.html

分享轮子-flutter下拉刷新上拉加载

flutter下拉上拉组件轮子 什么是flutter? 首先说下flutter,估计这个应该挺多人没听过flutter这个框架,它是一个google推出的跨平台的移动应用UI框架,和React Native是同样的目的,支持三大平台:Android,Ios,还有一个是google新出的系统,忘了叫什么...本人React Native也是用过…

linux块设备驱动中断程序,linux设备驱动归纳总结(六):1.中断的实现

linux设备驱动归纳总结(六)&#xff1a;1.中断的实现xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx一、什么是中断中断分两种&#xff1a;1)中断&#xff0c;又叫外部中断或异步中断&#xff0c;它的产生是由于外设向处理器发出中断…

(8)Python判断结构

转载于:https://www.cnblogs.com/hankleo/p/9170325.html