2010-06-02 32 views
1

我有從服務器的源代碼下面的C結構,和許多類似:什麼是最簡單的字節級操作?

// preprocessing magic: 1-byte alignment 

typedef struct AUTH_LOGON_CHALLENGE_C 
{ 
    // 4 byte header 
    uint8 cmd; 
    uint8 error;  
    uint16 size;  

    // 30 bytes 
    uint8 gamename[4]; 
    uint8 version1; 
    uint8 version2; 
    uint8 version3; 
    uint16 build; 
    uint8 platform[4]; 
    uint8 os[4]; 
    uint8 country[4]; 
    uint32 timezone_bias; 
    uint32 ip; 
    uint8 I_len; 

    // I_len bytes 
    uint8 I[1]; 
} sAuthLogonChallenge_C; 

// usage (the actual code that will read my packets): 
sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; // where buf is a raw byte array 

這些都是TCP數據包,我需要實現的東西,發射和在C#中讀取它們。最乾淨的方法是什麼?

我目前的做法涉及

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
unsafe struct foo { ... } 

和大量的fixed語句來讀取和寫入,但它感覺很笨重,而且由於數據包本身是不固定的長度,我不使用手感舒適它。此外,這是很多工作。

但是,它確實很好地描述了數據結構,並且協議可能會隨時間而改變,因此這可能是維護的理想選擇。

我有什麼選擇?只用C++編寫它並使用一些.NET魔術來使用它會更容易嗎?

說明:我還需要處理endian問題和空填充字符串。

回答

6

我會創建一個本地C#類來表示數據包及其數據(不是試圖匹配連線格式的數據),並將其傳遞給構造函數中的BinaryReader。有它在適當的塊從數據流中讀取其數據:

public class LogonChallenge 
{ 
    public LogonChallenge(BinaryReader data) 
    { 
     // header 
     this.Cmd = data.ReadByte(); 
     this.Error = data.ReadByte(); 
     this.Size = data.ReadUInt16(); 

     // etc 
    } 
} 

如果您有共享一個共同的頭部或其他領域的領先多種數據包類型,那麼你可以使用繼承來避免重複。 BasePacket類可能會讀取和填充標題字段,並且LogonChallenge類將從BasePacket繼承,並在調用基礎構造函數之後開始讀取挑戰字段。

1

如果有很多不安全的代碼,我可能會考慮用C++編寫代碼。可能作爲一個C++ COM DLL,如果需要,可以很容易地從C#調用它,只要確保COM接口很容易匹配.Net類型即可。雖然也許有更好的方式使用Managed C++,我從來沒有用過。

1

與ho1一致,我會寫一個包裝這個結構的小型C++/CLI類。這個類可能需要一個接口,它可以從字節數組中填充結構,以及每個結構成員的屬性。 C#客戶端可以從接收自套接字的字節數組構造此類實例,並將其中的每個結構成員作爲託管屬性讀取。所有的工作都可以在非託管代碼中完成。

0

好了,這裏是我想出:

abstract class Packet 
{ 
    protected enum T 
    { 
     Byte, 
     UInt16, 
     UInt32, 
     NullPaddedAsciiString, 
     Whatever 
    } 
    protected struct Offset 
    { 
     public int offset; 
     public T type;      // included only for readability 
     public Offset(int i, T type) 
     { 
      this.type = type; 
      offset = i; 
     } 
    } 

    protected byte[] data; 

    byte[] RawData { get { return data; } } 

    // getters and setters will be implemented using something like this 
    protected UInt16 GetUInt16(Offset o) 
    { 
     // magic 
    } 

    protected void Write(Offset o, string s) 
    { 
     // magic 
    } 
} 

class cAuthLogonChallenge : Packet 
{ 
    // still not perfect, but at least communicates the intent 
    static Offset cmd = new Offset(0, T.Byte); 
    static Offset error = new Offset(1, T.Byte); 
    static Offset size = new Offset(2, T.UInt16); 
    // etc. 

    public cAuthLogonChallenge(string username) 
    { 
     var size = 30 + username.Length 
     data = new byte[size]; 
     Write(cmd, 0x00); 
     // etc. 
    } 
} 
相關問題