C#和C++結構體Socket通訊

來源:互聯網
上載者:User

C#和C++結構體Socket通訊(2009-09-10 13:44:46)
轉載標籤: 雜談 分類: .net 
C#和C++結構體Socket通訊
2008-10-27 05:59
最近在用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();
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.