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();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM