C#和C++的Socket通信
最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。
1、仿照C++结构体写出C#的结构
[Serializable] // 指示可序列化 [StructLayout(LayoutKind.Sequential, Pack = 1)] // 按1字节对齐 public struct Operator { public ushort id; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] // 声明一个字符数组,大小为11 public char[] name; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] public char[] pass; public Operator(string user, string pass) // 初始化 { this.id = 4105; this.name = user.PadRight(11, '\0').ToCharArray(); this.pass = pass.PadRight(9, '\0').ToCharArray(); } }
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++结构体是:
typedef struct { WORD id; CHAR name[11]; CHAR password[9]; }Operator;
3、发送的时候先要把结构转换成字节数组
/// <summary> /// 将结构转换为字节数组 /// </summary> /// <param name="obj">结构对象</param> /// <returns>字节数组</returns> public static byte[] StructToBytes(object obj) { //得到结构体的大小 int size = Marshal.SizeOf(obj); //创建byte数组 byte[] bytes = new byte[size]; //分配结构体大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(size); //将结构体拷到分配好的内存空间 Marshal.StructureToPtr(obj, structPtr, false); //从内存空间拷到byte数组 Marshal.Copy(structPtr, bytes, 0, size); //释放内存空间 Marshal.FreeHGlobal(structPtr); //返回byte数组 return bytes; }
//接收的时候需要把字节数组转换成结构
/// <summary> /// byte数组转结构 /// </summary> /// <param name="bytes">byte数组</param> /// <param name="type">结构类型</param> /// <returns>转换后的结构</returns> public static object BytesToStruct(byte[] bytes, Type type) { //得到结构的大小 int size = Marshal.SizeOf(type); //byte数组长度小于结构的大小 if (size > bytes.Length) { //返回空 return null; } //分配结构大小的内存空间 IntPtr structPtr = Marshal.AllocHGlobal(size); //将byte数组拷到分配好的内存空间 Marshal.Copy(bytes, 0, structPtr, size); //将内存空间转换为目标结构 object obj = Marshal.PtrToStructure(structPtr, type); //释放内存空间 Marshal.FreeHGlobal(structPtr); //返回结构 return obj; }
4、实际操作:
using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; byte[] Message = StructToBytes(new Operator("user","pass")); // 将结构转换成字节数组 TcpClient socket = new TcpClient(); socket.Connect(ip,port); NetworkStream ns = Socket.GetStream(); ns.Write(Message,0,Message.Length); // 发送 byte[] Recv = new byte[1024]; // 缓冲 int NumberOfRecv = 0; IList<byte> newRecv = new List<byte>(); ns.ReadTimeout = 3000; try { do { // 接收响应 NumberOfRecv = ns.Read(Recv, 0, Recv.Length); for (int i = 0; i < NumberOfRecv; i++) newRecv.Add(Recv[i]); } while (ns.DataAvailable); byte[] resultRecv = new byte[newRecv.Count]; newRecv.CopyTo(resultRecv, 0); Operator MyOper = new Operator(); MyOper = (Operator)BytesToStruct(resultRecv, MyOper.GetType()); // 将字节数组转换成结构
在这里取值的时候可能会出现只能取到一个字段,剩余的取不到的问题,怎么回事我也搞不懂,反正我的解决办法就是按照字节的顺序从resultRecv里分别取出对应的字段的字节数组,然后解码,例如:
Operator.name是11个字节,最后一位是0,Operator.id是2个字节,那么从第3位到第12位的字节就是Operator.name的内容,取出另存为一个数组MyOperName,Encoding.Default.GetString(MyOperName)就是MyOper.name的内容。
socket.Close();
ns.Close();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理