最近在用C#做一个项目的时候Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流这个时候就需要用C#仿照C++的结构体做出一个结构来然后将其转换成二进制流进行发送之后将响应消息的二进制数据流转换成C#结构
仿照C++结构体写出C#的结构来
using SystemRuntimeInteropServices;
[Serializable] // 指示可序列化
[StructLayout(LayoutKindSequential Pack = )] // 按字节对齐
public struct Operator
{
public ushort id;
[MarshalAs(UnmanagedTypeByValArray SizeConst = )] // 声明一个字符数组大小为
public char[] name;
[MarshalAs(UnmanagedTypeByValArray SizeConst = )]
public char[] pass;
public Operator(string user string pass) // 初始化
{
thisid = ;
thisname = userPadRight( \)ToCharArray();
thispass = passPadRight( \)ToCharArray();
}
}
注意C#与C++数据类型的对应关系
C++与C#的数据类型对应关系表API数据类型类型描述C#类型API数据类型类型描述C#类型WORD位无符号整数ushortCHAR字符charLONG位无符号整数intDWORDLONG位长整数longDWORD位无符号整数uintHDC设备描述表句柄intHANDLE句柄位整数intHGDIOBJGDI对象句柄intUINT位无符号整数uintHINSTANCE实例句柄intBOOL位布尔型整数boolHWM窗口句柄intLPSTR指向字符的位指针stringHPARAM位消息参数intLPCSTR指向常字符的位指针StringLPARAM位消息参数intBYTE字节byteWPARAM位消息参数int
整个结构的字节数是bytes
对应的C++结构体是
typedef struct
{
WORD id;
CHAR name[];
CHAR password[];
}Operator;
发送的时候先要把结构转换成字节数组
using SystemRuntimeInteropServices;
/// <summary>
/// 将结构转换为字节数组
/// </summary>
/// <param name=obj>结构对象</param>
/// <returns>字节数组</returns>
public byte[] StructToBytes(object obj)
{
//得到结构体的大小
int size = MarshalSizeOf(obj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = MarshalAllocHGlobal(size);
//将结构体拷到分配好的内存空间
MarshalStructureToPtr(obj structPtr false);
//从内存空间拷到byte数组
MarshalCopy(structPtr bytes size);
//释放内存空间
MarshalFreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
接收的时候需要把字节数组转换成结构
/// <summary>
/// byte数组转结构
/// </summary>
/// <param name=bytes>byte数组</param>
/// <param name=type>结构类型</param>
/// <returns>转换后的结构</returns>
public object BytesToStruct(byte[] bytes Type type)
{
//得到结构的大小
int size = MarshalSizeOf(type);
Log(sizeToString() );
//byte数组长度小于结构的大小
if (size > bytesLength)
{
//返回空
return null;
}
//分配结构大小的内存空间
IntPtr structPtr = MarshalAllocHGlobal(size);
//将byte数组拷到分配好的内存空间
MarshalCopy(bytes structPtr size);
//将内存空间转换为目标结构
object obj = MarshalPtrToStructure(structPtr type);
//释放内存空间
MarshalFreeHGlobal(structPtr);
//返回结构
return obj;
}
实际操作
using SystemCollections;
using SystemCollectionsGeneric;
using SystemNet;
using SystemNetSockets;
byte[] Message = StructToBytes(new Operator(userpass)); // 将结构转换成字节数组
TcpClient socket = new TcpClient();
socketConnect(ipport);
NetworkStream ns = SocketGetStream();
nsWrite(MessageMessageLength); // 发送
byte[] Recv = new byte[]; // 缓沖
int NumberOfRecv = ;
IList<byte> newRecv = new List<byte>();
nsReadTimeout = ;
try
{
do
{
// 接收响应
NumberOfRecv = nsRead(Recv RecvLength);
for (int i = ; i < NumberOfRecv; i++)
newRecvAdd(Recv[i]);
}
while (nsDataAvailable);
byte[] resultRecv = new byte[newRecvCount];
newRecvCopyTo(resultRecv );
Operator MyOper = new Operator();
MyOper = (Operator)BytesToStruct(resultRecv MyOperGetType()); // 将字节数组转换成结构
在这里取值的时候可能会出现只能取到一个字段剩余的取不到的问题怎么回事我也搞不懂反正我的解决办法就是按照字节的顺序从resultRecv里分别取出对应的字段的字节数组然后解码例如
Operatorname是个字节最后一位是Operatorid是个字节那么从第位到第位的字节就是Operatorname的内容取出另存为一个数组MyOperNameEncodingDefaultGetString(MyOperName)就是MyOpername的内容
socketClose();
nsClose();