C#和C++结构体Socket通信
From:?http://hi.baidu.com/yangliangwang/blog/item/1a0116138ff098d6f6039ea9.html
最近在用C#做一個項目的時候,Socket發送消息的時候遇到了服務端需要接收C++結構體的二進制數據流,這個時候就需要用C#仿照C++的結構體做出一個結構來,然后將其轉換成二進制流進行發送,之后將響應消息的二進制數據流轉換成C#結構。
1、仿照C++結構體寫出C#的結構來
??? using System.Runtime.InteropServices;
??? [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 = 10000;
??????????? 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、發送的時候先要把結構轉換成字節數組
??????? using System.Runtime.InteropServices;?????
???????? /// <summary>
??????? /// 將結構轉換為字節數組
??????? /// </summary>
??????? /// <param name="obj">結構對象</param>
??????? /// <returns>字節數組</returns>
??????? public 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 object BytesToStruct(byte[] bytes, Type type)
??????? {
??????????? //得到結構的大小
??????????? int size = Marshal.SizeOf(type);
??????????? Log(size.ToString(), 1);
??????????? //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();
總結
以上是生活随笔為你收集整理的C#和C++结构体Socket通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中文全攻略
- 下一篇: WeakHashMap和Java引用类型