最近在用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. Django开发目录

    Django开发[第一章]:Django基础和基本使用 Django开发[第二章]:Django URLConf 进阶 Django开发[第三章]:Django View 进阶 Django开发[第四 ...

  2. codewars 题目笔记

    原题: Description: Bob is preparing to pass IQ test. The most frequent task in this test is to find ou ...

  3. 背水一战 Windows 10 (109) - 通知(Tile): 按计划显示 tile 通知, 轮询服务端以更新 tile 通知

    [源码下载] 背水一战 Windows 10 (109) - 通知(Tile): 按计划显示 tile 通知, 轮询服务端以更新 tile 通知 作者:webabcd 介绍背水一战 Windows 1 ...

  4. Javascript高级编程学习笔记(34)—— 客户端检测(3)用户代理检测

    用户代理检测 前面的文章介绍的是如何检测浏览器对某一功能的支持情况 但是在实践中我们有些时候免不了需要知道用户到底是用的什么浏览器对我们的站点进行访问 这也是统计用户行为的一部分 用户代理检测这种方式 ...

  5. Java初学者最佳的学习方法以及会遇到的坑(内含学习资料)!

    最近系统整理了一套java初学者最佳的学习方法以及会遇到的坑等,希望对你有所帮助. 目录: 一.学习java的前提 二.学习java的方法 三.学习java时的坑 四.学习java的路线(画重点) 一 ...

  6. 微信小程序web-view实例

    微信小程序web-view实例 index.js //index.js //获取应用实例 const app = getApp() Page({ /** * 页面的初始数据 */ data: { }, ...

  7. Metasploit Framework(1)基本命令、简单使用

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 基本的控制台命令介绍: banner 查看metasp ...

  8. Python - 使用pycallgraph生成函数关系图

    1- pycallgraph简介 可用于创建python函数关系图,依赖于dot命令,需要先安装 graphviz: HomePage:http://pycallgraph.slowchop.com/ ...

  9. java中调用三方接口post传参时map和jsonobject的区别转换

    post方法名及参数为:(具体方法可参考https://www.cnblogs.com/mufengforward/p/10510337.html) public static String doPo ...

  10. SVN切换账号

    问题背景 SVN账号在登录的时候,默认是保存在个人电脑的 C:\Users\Administrator\AppData\Roaming\Subversion\auth\svn.simple\ 目录下的 ...