最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结 构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。

1、仿照C++结构体写出C#的结构来

 
 1  using  System.Runtime.InteropServices;
 2  
 3      [Serializable]  //  指示可序列化 
 4      [StructLayout(LayoutKind.Sequential, Pack  =   1 )]  //  按1字节对齐 
 5       public   struct  Operator
 6  
 7       {
 8            public   ushort  id;
 9          [MarshalAs(UnmanagedType.ByValArray, SizeConst  =   11 )]  //  声明一个字符数组,大小为11 
10           public   char [] name;
11          [MarshalAs(UnmanagedType.ByValArray, SizeConst  =   9 )]
12           public   char [] pass;
13  
14            public  Operator( string  user,  string  pass)  //  初始化 
15           {
16               this .id  =   10000 ;
17               this .name  =  user.PadRight( 11 ,  ' /0 ' ).ToCharArray();
18               this .pass  =  pass.PadRight( 9 ,  ' /0 ' ).ToCharArray();
19          } 
20      } 
21  
22 

2、注意C#与C++数据类型的对应关系

C++与C#的数据类型对应关系表
API数据类型 类型描述 C#类型 API数据类型 类型描述 C#类型
WORD 16位无符号整数 ushort CHAR 字符 char
LONG 32位无符号整数 int DWORDLONG 64位长整数 long
DWORD 32位无符号整数 uint HDC 设备描述表句柄 int
HANDLE 句柄,32位整数 int HGDIOBJ GDI对象句柄 int
UINT 32位无符号整数 uint HINSTANCE 实例句柄 int
BOOL 32位布尔型整数 bool HWM 窗口句柄 int
LPSTR 指向字符的32位指针 string HPARAM 32位消息参数 int
LPCSTR 指向常字符的32位指针 String LPARAM 32位消息参数 int
BYTE 字节 byte WPARAM 32位消息参数 int

整个结构的字节数是22bytes。

对应的C++结构体是:

 
1  typedef  struct 
2  {
3       WORD id;            
4      CHAR name[ 11 ];
5      CHAR password[ 9 ];
6  } Operator;
7  

3、发送的时候先要把结构转换成字节数组

 
 1    using  System.Runtime.InteropServices;     
 2  
 3            ///   <summary> 
 4           ///  将结构转换为字节数组
 5           ///   </summary> 
 6           ///   <param name="obj"> 结构对象 </param> 
 7           ///   <returns> 字节数组 </returns> 
 8           public   byte [] StructToBytes( object  obj)
 9           {
10               // 得到结构体的大小 
11               int  size  =  Marshal.SizeOf(obj);
12               // 创建byte数组 
13               byte [] bytes  =   new   byte [size];
14               // 分配结构体大小的内存空间 
15              IntPtr structPtr  =  Marshal.AllocHGlobal(size);
16               // 将结构体拷到分配好的内存空间 
17              Marshal.StructureToPtr(obj, structPtr,  false );
18               // 从内存空间拷到byte数组 
19              Marshal.Copy(structPtr, bytes,  0 , size);
20               // 释放内存空间 
21              Marshal.FreeHGlobal(structPtr);
22               // 返回byte数组 
23               return  bytes;
24  
25         } 
26  
27 

接收的时候需要把字节数组转换成结构

 
 1  ///   <summary> 
 2           ///  byte数组转结构
 3           ///   </summary> 
 4           ///   <param name="bytes"> byte数组 </param> 
 5           ///   <param name="type"> 结构类型 </param> 
 6           ///   <returns> 转换后的结构 </returns> 
 7           public   object  BytesToStruct( byte [] bytes, Type type)
 8           {
 9               // 得到结构的大小 
10               int  size  =  Marshal.SizeOf(type);
11              Log(size.ToString(),  1 );
12               // byte数组长度小于结构的大小 
13               if  (size  >  bytes.Length)
14               {
15                   // 返回空 
16                   return   null ;
17              } 
18               // 分配结构大小的内存空间 
19              IntPtr structPtr  =  Marshal.AllocHGlobal(size);
20               // 将byte数组拷到分配好的内存空间 
21              Marshal.Copy(bytes,  0 , structPtr, size);
22               // 将内存空间转换为目标结构 
23               object  obj  =  Marshal.PtrToStructure(structPtr, type);
24               // 释放内存空间 
25              Marshal.FreeHGlobal(structPtr);
26               // 返回结构 
27               return  obj;
28          } 
29 

4、实际操作:

 
 1  using  System.Collections;
 2  using  System.Collections.Generic;
 3  using  System.Net;
 4  using  System.Net.Sockets;
 5  
 6  byte [] Message  =  StructToBytes( new  Operator( " user " , " pass " ));  //  将结构转换成字节数组 
 7  
 8  TcpClient socket  =   new  TcpClient();
 9  
10  socket.Connect(ip,port);
11  
12  NetworkStream ns  =  Socket.GetStream();
13  
14  ns.Write(Message, 0 ,Message.Length);  //  发送 
15  
16  byte [] Recv  =   new   byte [ 1024 ];  //  缓冲 
17  
18  int  NumberOfRecv  =   0 ;
19  
20  IList < byte >  newRecv  =   new  List < byte > ();
21  ns.ReadTimeout  =   3000 ;
22  try 
23  {
24  do 
25  {
26  //  接收响应 
27  NumberOfRecv  =  ns.Read(Recv,  0 , Recv.Length);
28  for  ( int  i  =   0 ; i  <  NumberOfRecv; i ++ )
29  newRecv.Add(Recv[i]);
30  } 
31  while  (ns.DataAvailable);
32  byte [] resultRecv  =   new   byte [newRecv.Count];
33  newRecv.CopyTo(resultRecv,  0 );
34  
35  Operator MyOper  =   new  Operator();
36  
37  MyOper  =  (Operator)BytesToStruct(resultRecv, MyOper.GetType());  //  将字节数组转换成结构 
38 

在这里取值的时候可能会出现只能取到一个字段,剩余的取不到的问题,怎么回事我也搞不懂,反正我的解决办法就是按照字节的顺序从 resultRecv里分别取出对应的字段的字节数组,然后解码,例如:

Operator.name是11个字节,最后一位是0,Operator.id是2个字节,那么从第3位到第12位的字节就是 Operator.name的内容,取出另存为一个数组 MyOperName,Encoding.Default.GetString(MyOperName)就是MyOper.name的内容。

 
1  socket.Close();
2  
3  ns.Close();

C#.NET和C++结构体Socket通信与数据转换的更多相关文章

  1. C/C++编程笔记:C语言对齐问题【结构体、栈内存以及位域对齐】

    引言 考虑下面的结构体定义: 假设这个结构体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4. 现在,我们编写一个简单的程序: 运行后输出: 为什么会这 ...

  2. java socket传送一个结构体给用C++编写的服务器解析的问题

    另一端是Java写客户端程序,两者之间需要通信.c++/c接收和发送的都是结构体,而Java是直接发送的字节流或者byte 数组.解决方法:c++/c socket 在发送结构体的时候其实发送的也是字 ...

  3. C# Socket 入门4 UPD 发送结构体(转)

    今天我们来学 socket  发送结构体 1. 先看要发送的结构体 using System; using System.Collections.Generic; using System.Text; ...

  4. socket编程——sockaddr_in结构体操作

    sockaddr结构体 sockaddr的缺陷: struct sockaddr 是一个通用地址结构,这是为了统一地址结构的表示方法,统一接口函数,使不同的地址结构可以被bind() , connec ...

  5. socket编程相关的结构体和字节序转换、IP、PORT转换函数

    注意:结构体之间不能直接进行强制转换, 必须先转换成指针类型才可以进行结构体间的类型转换, 这里需要明确的定义就是什么才叫强制转换. 强制转换是将内存中一段代码以另一种不同类型的方式进行解读, 因此转 ...

  6. Linux C Socket编程发送结构体、文件详解及实例

    利用Socket发送文件.结构体.数字等,是在Socket编程中经常需要用到的.由于Socket只能发送字符串,所以可以使用发送字符串的方式发送文件.结构体.数字等等. 本文:http://www.c ...

  7. C#与C++通过socket传送结构体

    C#服务端: using System; using System.Net.Sockets; using System.Net; using System.IO; using System.Diagn ...

  8. 2. socket结构体——表示socket地址

    一.两种通用socket结构体 1. sockaddr struct sockaddr { sa_family_t sa_family; // 地址族 char sa_data[14]; // 存放s ...

  9. struct socket结构体详解

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://weiguozhihui.blog.51cto.com/3060615/15852 ...

随机推荐

  1. 重构SP5中声明自定义扩展类为API调用

    展的自定义类方法想要被前端异步调用必须在方法中 1.首先类必须继承自 AppService 类 2.方法必须指定特性 [ActionDescription("名称","编 ...

  2. karaf 控制台 常用linux指令(1)

    0,切换命令当前域名 -bash代码 *:feature 命令是由域名+命令名组成的,切换当前域名,比如切换到*:feature,那么再输入list,则执行的是feature:list,切换到*:bu ...

  3. 简单了解Django

    Django 是开源代码web应用的框架,由python完成,django的主要目的是简便,快速开发数据库驱动网站 主要用于测试,运维,自测. 1.下载Django. 个人建议使用命令pip inst ...

  4. tensorflow安装过程cpu版-(windows10环境下)---亲试可行方案

    tensorflow安装过程cpu版-(windows10环境下)---亲试可行方案   一, 前言:本次安装tensorflow是基于Python的,安装Python的过程不做说明 二, 安装环境: ...

  5. Educational Codeforces Round 25

    A 题意:给你一个01的字符串,0是个分界点,0把这个字符串分成(0的个数+1)个部分,分别求出这几部分1的个数.例如110011101 输出2031,100输出100,1001输出101 代码: # ...

  6. 手工检测SQL注入漏洞

    SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,具体来说,它是利用现有应用程序将(恶意的)SQL命令注入到后台数据库引擎执 ...

  7. java责任链模式及项目实际运用

    1.前言 上次我们认识了java责任链模式的设计,那么接下来将给大家展示责任链模式项目中的实际运用.如何快速搭建责任链模式的项目中运用. 2.简单技术准备 我们要在项目中使用借助这样的几个知识的组合运 ...

  8. win10怎么查看激活到期时间如何看是否永久激活

    win10怎么查看激活到期时间如何看是否永久激活     我们知道Windows系统需要激活后才可以使用全部功能,那么你的Windows10激活了吗?如何查看激活时间呢?是不是永久激活的?带着这些问题 ...

  9. 第69节:Java中数据库的多表操作

    第69节:Java中数据库的多表操作 前言 学习数据库的多表操作,去电商行业做项目吧!!! 达叔,理工男,简书作者&全栈工程师,感性理性兼备的写作者,个人独立开发者,我相信你也可以!阅读他的文 ...

  10. DevOps - 参考信息

    DevOps DevOps(Development+Operations)强调共同对业务目标负责,以实现用户价值作为唯一的评判标准:保证产品功能及时实现.成功部署和稳定使用: 是一种重视软件开发人员( ...