【问题标题】:What's the cleanest way to do byte-level manipulation?进行字节级操作的最干净的方法是什么?
【发布时间】:2010-06-02 11:55:54
【问题描述】:

我有以下来自服务器源代码的 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 魔法来使用它会更容易吗?

澄清:我还需要处理字节序问题和空填充字符串。

【问题讨论】:

    标签: c# marshalling bytearray byte


    【解决方案1】:

    我将创建一个本地 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 继承并在调用基本构造函数后开始读取挑战字段。

    【讨论】:

      【解决方案2】:

      如果有很多不安全的代码,我可能会考虑用 C++ 编写它。可能作为一个 C++ COM DLL,如果需要,可以很容易地从 C# 调用,只需确保 COM 接口易于匹配 .Net 类型。尽管使用托管 C++ 可能有更好的方法,但我从未使用过。

      【讨论】:

        【解决方案3】:

        同意 ho1,我会写一个小的 C++/CLI 类来包装这个结构。这个类可能需要一个接口,它可以从字节数组中填充结构,以及每个结构成员的属性。 C# 客户端可以从套接字接收的字节数组构造此类实例,并从中读取每个结构成员作为托管属性。所有繁重的工作都可以在非托管代码中完成。

        【讨论】:

          【解决方案4】:

          好的,这就是我想出的:

          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.
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2011-12-02
            • 2012-04-16
            • 2017-02-20
            • 2013-10-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-19
            • 2014-05-03
            相关资源
            最近更新 更多