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
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
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