最近在用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. 6 week work 2

    CSS颜色表示法和颜色表(调色板) 1.用颜色名表示 如:white.red.greenyellow.gold等. 2.用十六进制的颜色值表示(红.绿.蓝) #FF0000或者#F00 3.用rgb( ...

  2. Express实例代码分析1——简单的用户验证登录文件

    /** * Module dependencies. */ var express = require('../..');// ../..是上级目录的上级目录 var hash = require(' ...

  3. linux根目录介绍

    1. /bin binary二进制 存放系统许多可执行程序文件 执行的相关指令,例如ls pwd whoami,后台的支持文件目录 2. /sbin super binary超级的二进制 存放系统许多 ...

  4. 与Servlet相关的类

    有4个有关的类,通过servlet可以获得其中的三个,然后通过ServletConfig间接获取ServletContext.1. ServletConfig该类是在Servlet容器初始化Servl ...

  5. Postman+Newman+jenkins实现API自动化测试

    最近自己在学习用postman+newman+jenkins实现API自动化测试,这里做个回顾和记录.(此次是在windows上进行的环境搭建) 一.说明 1.大致思路:利用postman做接口调试所 ...

  6. BI项目记(二):给我接套数据

    这次故事的主角还是小D,小D工作在一家传统公司的信息部门,负责数据仓库系统的运维和开发. 话说有一天,小D被教导老板的office,老板给布置了一个任务,让小D在现有数据仓库里接入刚上线的两个系统的数 ...

  7. 打开Python IDLE时的错误:Subprocess Startup Error

    比较常见的是这个 方法1: 修改[Python目录]\Lib\idlelib\PyShell.py文件,在1300行附近,将def main():函数下面 use_subprocess = True ...

  8. 7.侧滑、ViewDragHelper、属性动画

    实现这样的效果: ## 侧滑面板(对ViewGroup的自定义)* 应用场景: 扩展主面板的功能* 功能实现: > 1. ViewDragHelper: Google2013年IO大会提出的, ...

  9. rabbitmq在ios中实战采坑

    1. rabbitmq在ios中实战采坑 1.1. 问题 ios使用rabbitmq连接,没过多久就断开,并报错.且用android做相同的步骤并不会报错,错误如下 Received connecti ...

  10. 数据库语句收藏(MySQL)

    概览 => MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性. =>关键字不区分大小写 => S ...