6

我一直在研究旨在以 C 语言实现并分别以 C 风格定义的网络协议规范,例如:

struct some_header {
  uint8_t version;
  uint8_t type;
  uint16_t length;
  uint32_t id; /
};

struct some_struct {
  struct some_header header;
  uint8_t field1; 
  uint8_t pad[3]; /* Pad to 32 bits */
  uint32_t field2;
};

规范中定义了大约数百个类似“some_struct”的结构。

现在的目标是复制 C# 中的定义并围绕这些结构增加一些功能。

假设将它们表示为对象会很好,那么在 C# 中使用什么来表示上面示例中的结构的最佳原语是什么?

  1. 使用 unsafe 子句复制类似 C 的定义,然后在每个结构周围使用类包装器

  2. 使用 StructLayout 属性来指导托管结构内的布局,并为每个此类结构使用包装类

  3. 使用成熟的课程,并且只使用内存或网络流进行有线表示

除了托管 c++ 之外,还有其他想法吗?

其次,显然任务本身是非常劳动密集型的,并且在列出的任何情况下都容易出错。是否有任何自动化可以帮助将 C 标头转换为上述解决方案之一?请问有人可以推荐一个吗?

UPD。

一些图纸可以更好地描述问题。

(BoxA) --- 通过 TCP/IP 运行的有线协议 --- (BoxB)

假设 BoxA 运行一些用纯 C 编写的代码,BoxB 应该运行用 C# 编写的代码。

有线协议代表了这个庞大的规范,它有 100 多种结构来通信 BoxA 和 BoxB。如果协议是用 Google Protocol Buffers 或 Apache Thrift 定义的,那可能会容易得多,但不幸的是,我正在处理按原样定义的标准。

4

5 回答 5

4

恕我直言,最好手动转换它们,如下所示:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct Header
{
    public Byte version;
    public Byte type;
    public UInt16 length;
    public UInt32 id;
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct Struct
{
    public Header header;
    public Byte field1;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
    public Byte[] pad;
    public UInt32 field2;
}

将来您可能不得不将它们与本机函数一起使用......所以最好将它们保留为结构并直接在您的 .NET 代码中使用它们。如果您必须将它们作为本机方法的参数传递或将结果 IntPtrs 转换回它们( Marshal.PtrToStructure),它们将非常有用。

于 2013-01-15T18:31:52.687 回答
1

是否有任何自动化可以帮助将 C 标头转换为上述解决方案之一?

使用 C 预处理器获取一个大文件,然后使用脚本剪切结构定义,然后应用搜索和替换来转换类型并添加属性。

于 2013-01-15T18:42:02.970 回答
0

关于自动化问题,我刚刚找到了从 c-header 生成 PInvoke 样式代码的优秀工具

http://clrinterop.codeplex.com/releases/view/14120#ReviewsAnchor

具体来说,需要在命令行使用以下工具

sigimp /lang:cs "路径到 c 头"

下面是生成的代码示例:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct some_header {

/// uint8_t->unsigned char
public byte version;

/// uint8_t->unsigned char
public byte type;

/// uint16_t->unsigned int
public uint length;

/// uint32_t->unsigned int
public uint id;
}

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)]
public some_struct {


public some_header header;

/// uint64_t->unsigned int
public uint xxx;

/// uint32_t->unsigned int
public uint xxx2;

/// uint8_t->unsigned char
public byte xxx3;

/// uint8_t->unsigned char
public byte xxx4;

/// uint8_t[2]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=2)]
public string pad;

/// uint32_t->unsigned int
public uint xxx5;

/// uint32_t->unsigned int
public uint xxx6;
}
于 2013-01-16T20:56:46.393 回答
0

我的倾向是使用封装字节数组引用和索引的类型,然后有方法读取或写入下一个字节/字/长字/任何内容。从字节流转换为结构类型的代码如下所示:

myReader = new ByteArrayReader(myArr);
myHead.version = myReader.getByte();
myHead.type; = myReader.getByte();
myHead.length = myReader.getUInt16();
myHead.id = myReader.getUInt32();

在这种情况下,我假设myHead将是具有上述公共字段的透明结构。我不建议将标头类型设为类,除非实例实际上要“做”某事。使阅读器类型成为结构可能会提高性能,但在某些情况下可能会导致令人惊讶的语义。框架中最接近的等价物是List<T>.Enumerator,这是出于性能原因的结构。

于 2013-01-15T20:36:28.503 回答
-2

虽然 C# 有Struct,但最好的办法是使用Class. 您可以将类用作数据容器,查看贫血域模式,或者您可以在其中包含实现,例如验证和业务逻辑。请参阅:域驱动。

在处理您的领域模型时,请确保接受SOLID 原则

此外,根据您的领域模型,您当然可以利用抽象、继承等。

哦。保持简单和直观。

于 2013-01-15T18:30:18.063 回答