2010-06-02 30 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. 
    } 
} 
相关问题