C#.NET和C++结构体Socket通信与数据转换
最近在用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
Operator6
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
8
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();4

本文详细介绍了使用C#和C++进行Socket通信时,如何通过结构体实现二进制数据的序列化与反序列化过程,包括结构体的定义、数据类型对应关系、数据转换方法以及实际操作示例。

1475

被折叠的 条评论
为什么被折叠?



