最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。
1、仿照C++结构体写出C#的结构来
![]()
Code
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++结构体是:
![]()
Code
1
typedef struct
2![]()
![]()
{
3
WORD id;
4
CHAR name[11];
5
CHAR password[9];
6
}Operator;
7![]()
8![]()
3、发送的时候先要把结构转换成字节数组
![]()
Code
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![]()
接收的时候需要把字节数组转换成结构
![]()
Code
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、实际操作:
![]()
Code
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的内容。
![]()
Code
1
socket.Close();
2![]()
3
ns.Close();
4![]()