最近在用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. OC内存管理、非ARC机制、MRR机制

    在Xcode里面,默认为ARC(auto reference counting),也就是自动内存管理机制,在这里我们要了解的是内存管理,肯定是不能让系统帮我们管理内存,我们需要将ARC关闭,首先在左边 ...

  2. List、Set、数据结构、Collections

    一.数据结构: 1.什么是数据结构: 一种数据的存储方式 2.常见的4+1种数据结构 堆栈结构: 它是只有一个开口的容器结构 特点: 先进后出(FILO) 例子:弹夹,桶装可比克 队列结构: 它是两端 ...

  3. 录音--获取语音流(pyAudio)

    这是学习时的笔记,包含相关资料链接,有的当时没有细看,记录下来在需要的时候回顾. 有些较混乱的部分,后续会再更新. 欢迎感兴趣的小伙伴一起讨论,跪求大神指点~ 录音-语音流(pyAudio) tags ...

  4. hive -help hive命令行执行sql参数

    在shell命令行执行 hive -help 结果如下: -d,--define <key=value> Variable substitution to apply to Hive co ...

  5. 第一课:Python入门(笔记)

    一.变量 1.什么是变量 #变量即变化的量,核心是“变”与“量”二字,变即变化,量即衡量状态. 2.为什么要有变量 #程序执行的本质就是一系列状态的变化,变是程序执行的直接体现,所以我们需要有一种机制 ...

  6. Java 多线程之悲观锁与乐观锁

    一.悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源 ...

  7. Android 通过onTouchEvent判断是否为双击事件

    通过onTouchEvent判断是否为双击事件,通过前面一次up 跟本次down之间的时间差,并且点击的点的坐标偏移来确定是否为双击事件DOUBLE_TAP_TIMEOUT. boolean isMo ...

  8. PHP全栈学习笔记18

    php基础知识,JavaScript,jQuery,ajax基础知识 linux基础知识,mysql数据库的基础与优化 程序设计,PHP框架基础知识,算法,逻辑思维,高并发 PHP基础知识 引用变量, ...

  9. CentOS随笔——关机命令

    关机重启命令 在linux领域内大多用在服务器上,很少遇到关机的操作.毕竟服务器上跑一个服务是永无止境的,除非特殊情况面,不得已才会关机. 正确的关机流程为:sync>shutdown>r ...

  10. 机器学习入门11 - 逻辑回归 (Logistic Regression)

    原文链接:https://developers.google.com/machine-learning/crash-course/logistic-regression/ 逻辑回归会生成一个介于 0 ...