首页 文章

将C结构转换为C#以用于UDP通信

提问于
浏览
4

我正在编写一个程序,它将通过UDP与C程序通信 . 另一个程序已经写好了(不是我) . 我有一个.h文件,它定义了两个用于数据的结构 .

编辑:这意味着我无法更改数据格式 . 我需要能够根据C .h文件结构进行读写!

我怎么用C#做这个?我将以这种格式发送和接收数据 .

struct mdata
{
  uint32_t  mark_kupnr;
  uint16_t  mark_provnr;
  uint16_t  markriktning;
  uint16_t  xpos;
  uint16_t  ypos;
};

typedef struct  
{
  uint32_t      kupnr;
  uint16_t      lngd;
  uint16_t      bredd;
  uint16_t      tjocklek;
  char          slagkraft;
  uint8_t       antal;
  struct mdata  mark[10];
} markdata;

编辑:我试图在C#中创建相应的结构,但它不起作用

顺便说一句,我在Windows上运行,而C程序在Linux上运行 . 在“规范”中,它表示数据应该是Little Endian .

struct mdata
{
  UInt32    mark_kupnr;
  UInt16    mark_provnr;
  UInt16    markriktning;
  UInt16    xpos;
  UInt16    ypos;
};

typedef struct  
{
  UInt32        kupnr;
  UInt16        lngd;
  UInt16        bredd;
  UInt16        tjocklek;
  char          slagkraft;
  byte          antal;
  // here I have some trouble
  mdata[]   mark[10]; //???
} markdata;

3 回答

  • 2

    听起来好像你正在寻找fixed keyword . C#中的 char 也是C中 char 的两倍,因此您需要使用正确的相应类型 sbyte .

    这是你的第二个结构的定义方式:

    struct markdata
    {
      UInt32        kupnr;
      UInt16        lngd;
      UInt16        bredd;
      UInt16        tjocklek;
      sbyte         slagkraft;
      byte          antal;
      fixed   mdata mark[10];
    }
    

    如果您得到错误的值,请检查它们是否是字节交换的 . 但基于使用的系统和数据格式规范,默认情况下,字节序很可能是正确的 .

  • 1

    如果将结构序列化为与平台无关的格式,则可以避免所有容易出错和低级别的数据对齐问题 .

    考虑使用Google Protocol BuffersApache Thrift自动生成序列化/反序列化代码 . 这些工作整齐地跨越多种语言和数据类型,被广泛使用(测试和调试) . 这些库中的每一个都要求您以与Microsoft IDL类似的格式保留结构的主蓝图 . 然后,您可以轻松地为任何语言生成互操作代码 .

    我个人对这两个库都是个问题,所以我选择将数据序列化为JSON format并通过线路发送/接收JSON作为0终止字符串 . 我使用了小的,仅限 Headers 的RapidJSON C库,并使用RapidJSON手动实现了序列化和反序列化代码 .

  • 0

    如果您的C程序主机是IBM-PC兼容机器,则必须在C#客户端上执行字节交换 . 如果不是你必须没有 .

    要与C#程序通信,必须将读取的数据反转为大于1个字节,因为C#使用big-endian字节顺序而不是本地C的little-endian . 要交换WORD(16位)和DWORD(32位),您可以使用此宏

    // BYTE SWAPING IN WORD and DWORD
    
    #define WORD_SWAP(x)    ((((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00))
    
    #define DWORD_SWAP(x)   ((((x) >> 24) & 0x000000ff) | \
                             (((x) >> 8) & 0x0000ff00) | \
                             (((x) << 8) & 0x00ff0000) | \
                             (((x) << 24) & 0xff000000))
    

    或C#中的类似功能 . 仅适用于单独的结构字段 .

    PS:不要忘记编译器打包功能来纠正双方的数据含义 .

    欲了解更多信息,请访问EndiannessData structure alignment

    所以人们要求我在C#中做同样的事情

    // Swaps uint16_t
    ushort Swap(ushort x)
    {
         return (ushort)((((x) >> 8) & 0x00ff) | (((x) << 8) & 0xff00));
    }
    
    // Swaps uint32_t
    uint Swap(uint x)
    {
         return ((((x) >> 24) & 0x000000ff) |
                 (((x) >> 8) & 0x0000ff00) |
                 (((x) << 8) & 0x00ff0000) |
                 (((x) << 24) & 0xff000000));
    }
    

    使用C#结构 client 表示 UDPClient 对象 . 这一切都必须以低级别的方式进行 .

    struct mdata
    {
      UInt32  mark_kupnr;
      UInt16  mark_provnr;
      UInt16  markriktning;
      UInt16  xpos;
      UInt16  ypos;
    }
    
    // example of mdata read with sockets
    mbata data;
    var bytes = client.Receive()
    
    data.mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
    data.mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
    data.markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
    data.xpos = Swap(BitConverter.ToUInt16(bytes, 8));
    data.ypos = Swap(BitConverter.ToUInt16(bytes, 10));
    

    表格输出数据必须从 UInt32UInt16 反向到字节数组 . 然后使用 List<byte>.AddRange(variable_byte_array); 以正确的顺序合并所有这些,而不是将此形成的列表转换为字节数组并使用 UDPClient.Send(formed_array); .

    C中的数组按此顺序放置在内存中 [0][1][2][3][4][5][6][7][8][9] . 所以你必须从0到9之间连续读取10次mdata结构 .

    // read first data
    typedef struct  
    {
      UInt32        kupnr;
      UInt16        lngd;
      UInt16        bredd;
      UInt16        tjocklek;
      sbyte          slagkraft;
      byte          antal;
      // now read mkdata 10 times
      struct mdata  mark[10];
    } markdata; // we have done with markdata
    

    没有人认为你使用类而不是结构 . 考虑到我们完全使用字节(二进制)序列化,处理数组会更简单 . 比添加到每个类适当的方法(例如)

    mdata类或结构方法

    void Receive(UDPClient client)
    {
        var bytes = client.Receive()
    
        mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
        mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
        markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
        xpos = Swap(BitConverter.ToUInt16(bytes, 8));
        ypos = Swap(BitConverter.ToUInt16(bytes, 10));
    }
    
    void Send(UDPClient client)
    {
        var listOfBytes = new List<byte>();
    
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_kupnr)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_provnr)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(markriktning)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(xpos)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(ypos)));
    
        client.Send(listOfBytes.ToArray(), listOfBytes.Count);
    }
    
    void Receive(byte[] bytes)
    {      
        mark_kupnr = Swap(BitConverter.ToUInt32(bytes, 0));
        mark_provnr = Swap(BitConverter.ToUInt16(bytes, 4));
        markriktning = Swap(BitConverter.ToUInt16(bytes, 6));
        xpos = Swap(BitConverter.ToUInt16(bytes, 8));
        ypos = Swap(BitConverter.ToUInt16(bytes, 10));
    }
    
    void Send(out byte[] bytes)
    {
        var listOfBytes = new List<byte>();
    
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_kupnr)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(mark_provnr)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(markriktning)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(xpos)));
        listOfBytes.AddRange(BitConverter.GetBytes(Swap(ypos)));
    
        bytes = listOfBytes.ToArray();
    }
    

    您的 markdata 类中的数组不会被修复,因此您只需在类中创建所需数量的元素即可 . 所有脏工作都将转到发送和接收方法 .

    要处理数组,您可以添加字节移位值来逐步执行 mdata 数组 .

相关问题