在 C/C++ 中解析二进制消息流

2024-03-16

我正在编写二进制协议(Javad GRIL 协议)的解码器。它由大约一百条消息组成,数据格式如下:

struct MsgData {
    uint8_t num;
    float x, y, z;
    uint8_t elevation;
    ...
};

这些字段是 ANSI 编码的二进制数字,彼此连续且没有间隙。解析此类消息的最简单方法是将输入字节数组转换为适当的类型。问题在于流中的数据是打包的,即未对齐的。

在 x86 上,可以使用以下方法解决#pragma pack(1)。但是,这在其他一些平台上不起作用,或者会因进一步处理未对齐的数据而产生性能开销。

另一种方法是为每种消息类型编写特定的解析函数,但正如我所提到的,该协议包含数百条消息。

另一种选择是使用 Perl 之类的东西unpack()函数并将消息格式存储在某处。说,我们可以#define MsgDataFormat "CfffC"然后打电话unpack(pMsgBody, MsgDataFormat)。这要短得多,但仍然容易出错且冗余。此外,格式可能会更复杂,因为消息可以包含数组,因此解析器将变得缓慢且复杂。

有没有通用且有效的解决方案?我读了这个帖子 https://stackoverflow.com/questions/321423/parsing-binary-data-in-c并用谷歌搜索了一下,但没有找到更好的方法。

也许C++有解决方案?


好的,以下是我使用 VC10 和 GCC 4.5.1 编译的(在 ideone.com 上 http://ideone.com/m0g2P)。我认为C++1x的所有这些需求是<tuple>,这应该是可用的(如std::tr1::tuple)在较旧的编译器中也是如此。

它仍然需要您为每个成员键入一些代码,但这是非常少的代码。 (见最后我的解释。)

#include <iostream>
#include <tuple>

typedef unsigned char uint8_t;
typedef unsigned char byte_t;

struct MsgData {
    uint8_t num;
    float x;
    uint8_t elevation;

    static const std::size_t buffer_size = sizeof(uint8_t)
                                         + sizeof(float) 
                                         + sizeof(uint8_t);

    std::tuple<uint8_t&,float&,uint8_t&> get_tied_tuple()
    {return std::tie(num, x, elevation);}
    std::tuple<const uint8_t&,const float&,const uint8_t&> get_tied_tuple() const
    {return std::tie(num, x, elevation);}
};

// needed only for test output
inline std::ostream& operator<<(std::ostream& os, const MsgData& msgData)
{
    os << '[' << static_cast<int>(msgData.num) << ' ' 
       << msgData.x << ' ' << static_cast<int>(msgData.elevation) << ']';
    return os;
}

namespace detail {

    // overload the following two for types that need special treatment
    template<typename T>
    const byte_t* read_value(const byte_t* bin, T& val)
    {
        val = *reinterpret_cast<const T*>(bin);
        return bin + sizeof(T)/sizeof(byte_t);
    }
    template<typename T>
    byte_t* write_value(byte_t* bin, const T& val)
    {
        *reinterpret_cast<T*>(bin) = val;
        return bin + sizeof(T)/sizeof(byte_t);
    }

    template< typename MsgTuple, unsigned int Size = std::tuple_size<MsgTuple>::value >
    struct msg_serializer;

    template< typename MsgTuple >
    struct msg_serializer<MsgTuple,0> {
        static const byte_t* read(const byte_t* bin, MsgTuple&) {return bin;}
        static byte_t* write(byte_t* bin, const MsgTuple&)      {return bin;}
    };

    template< typename MsgTuple, unsigned int Size >
    struct msg_serializer {
        static const byte_t* read(const byte_t* bin, MsgTuple& msg)
        {
            return read_value( msg_serializer<MsgTuple,Size-1>::read(bin, msg)
                             , std::get<Size-1>(msg) );
        }
        static byte_t* write(byte_t* bin, const MsgTuple& msg)
        {
            return write_value( msg_serializer<MsgTuple,Size-1>::write(bin, msg)
                              , std::get<Size-1>(msg) );
        }
    };

    template< class MsgTuple >
    inline const byte_t* do_read_msg(const byte_t* bin, MsgTuple msg)
    {
        return msg_serializer<MsgTuple>::read(bin, msg);
    }

    template< class MsgTuple >
    inline byte_t* do_write_msg(byte_t* bin, const MsgTuple& msg)
    {
        return msg_serializer<MsgTuple>::write(bin, msg);
    }
}

template< class Msg >
inline const byte_t* read_msg(const byte_t* bin, Msg& msg)
{
    return detail::do_read_msg(bin, msg.get_tied_tuple());
}

template< class Msg >
inline const byte_t* write_msg(byte_t* bin, const Msg& msg)
{
    return detail::do_write_msg(bin, msg.get_tied_tuple());
}

int main()
{
    byte_t buffer[MsgData::buffer_size];

    std::cout << "buffer size is " << MsgData::buffer_size << '\n';

    MsgData msgData;
    std::cout << "initializing data...";
    msgData.num = 42;
    msgData.x = 1.7f;
    msgData.elevation = 17;
    std::cout << "data is now " << msgData << '\n';
    write_msg(buffer, msgData);

    std::cout << "clearing data...";
    msgData = MsgData();
    std::cout << "data is now " << msgData << '\n';

    std::cout << "reading data...";
    read_msg(buffer, msgData);
    std::cout << "data is now " << msgData << '\n';

    return 0;
}

对我来说这打印



buffer size is 6
initializing data...data is now [0x2a 1.7 0x11]
clearing data...data is now [0x0 0 0x0]
reading data...data is now [0x2a 1.7 0x11]
  

(我已经缩短了你的MsgData类型仅包含三个数据成员,但这仅用于测试。)

对于每种消息类型,您需要定义其buffer_size静态常数和两个get_tied_tuple()成员函数,一const和一个非const,两者的实现方式相同。 (当然,这些也可以是非成员,但我试图让它们靠近它们所绑定的数据成员列表。)
对于某些类型(例如std::string)你需要添加这些的特殊重载detail::read_value() and detail::write_value()功能。
对于所有消息类型,其余机制保持不变。

有了完整的 C++1x 支持,您也许能够摆脱必须完全键入get_tied_tuple()成员函数,但我还没有实际尝试过。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C/C++ 中解析二进制消息流 的相关文章

随机推荐

  • 共享指针递归删除递归数据结构导致堆栈溢出

    我有许多长链接列表 它们最多有 20 000 个项目 它们有不同的起点 但最终可以从某个节点开始指向同一个节点 我决定让这样的链表一起成长并共享它们之间的记忆 这就是为什么我决定使用共享指针实现链表 include
  • Google 或其他搜索引擎执行 JavaScript 吗?

    我只是想知道 Google 或其他搜索引擎是否在您的网页上执行 JavaScript 例如 如果您使用 JavaScript 设置标题标签 Google 搜索引擎会看到它吗 出于 SEO 目的进行的一些实验表明 至少大公司 例如 Googl
  • 将 IDENTITY 转换为数据类型 int 时出现算术溢出错误 [已关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 通过 vb 代码运行时 将 IDENTITY 转换为数据类型 int 时发生算术溢出错误 但是当我执行存储过程时 数据会
  • AngularJS - 获取字段的标签文本

    Question 我想知道 AngularJS 获取字段标签的 最佳实践 方法是什么 使用 jQuery 您只需使用 label for 查询进行查询 然后提取文本 虽然用 AngularJS 可以做到这一点 但总感觉有些不对劲 假设您的
  • 使用 Python 将带有内嵌图像的电子邮件发送到 Gmail

    我的目标是使用 Python 向具有内嵌图像的 Gmail 用户发送电子邮件 无法在线托管此图像 然后通过href 由于图像的敏感性 来自我的工作的数据 我尝试过编码base64版本变成HTML然后发送的是HTML 但众所周知这是行不通的
  • 错误:GooglePlayServicesUtil:发生内部错误。请参阅日志以获取详细信息。我该如何解决?

    我花了无数的时间试图弄清楚这个 google Drive android api 并且我一直在试图弄清楚如何使用它 这让我自己感到非常沮丧 我正在使用入门链接 https developers google com drive androi
  • 无法使用 selenium 和 read_html 从宏观趋势检索数据来创建数据框?

    我想将宏观趋势数据导入 pandas 数据框架 从网站的页面源来看 数据似乎位于 jqxgrid 中 我尝试使用 pandas beautiful soup 和 read html 函数 但没有找到表 我目前正在尝试使用硒来提取数据 我希望
  • 如何解析/编码二进制消息格式?

    我需要用 Java 解析和编码为旧的二进制消息格式 我开始使用 DataOutputStream 来读取 写入基元类型 但我遇到的问题是消息格式与字节偏移量不能很好地对齐并且包含位标志 例如我必须处理这样的消息 uint32 b b uin
  • 如果 setUpClass 抛出异常,如何使 python 单元测试失败

    我在使用 python setUpClass 时遇到了一些问题 例如考虑以下情况 class MyTest unittest case TestCase classmethod def setUpClass cls print Test s
  • 定义一个不平凡的 Scala 案例类是否合适?

    我今天正在定义一个 Scala 类 我想 我需要一个equals方法和一个hashCode方法 和一个copy方法也会很方便 我将把它变成一个案例类 我的类已经有一堆其他代码 而且绝不是微不足道的 很好 一切都有效 但是当教科书处理案例类时
  • 获取Makefile中的源代码结构

    我正在开发一个 C 项目 我决定将源代码及其对象放在不同的目录中 根目录有类似这样的内容 SmartC tree L 1 built doc Makefile README md src tests trash 因此 在 src 和buil
  • Type.GetFields() - 只返回“public const”字段

    我想调用 Type GetFields 并且只返回声明为 public const 的字段 到目前为止我有这个 type GetFields BindingFlags Static BindingFlags Public 但这还包括 公共静
  • 访问不存在的缩略图

    我制作了一个应用程序 可以向您显示计算机中的文件列表 每当您单击列表中的任何项目时 它旁边的小图片框都会显示相应文件的缩略图 我在 Windows 7 上使用 C 为了获取缩略图 我重复使用了另一个问题中发布的方法 首先 我参考了 Wind
  • Swagger 参数和复杂类型

    在下面的 Swagger 定义中 我需要参数labelValue属于类型LabelValueObject 以便对其进行验证并正确反序列化 但是 我无法弄清楚语法 那怎么办呢 swagger 2 0 paths competition pos
  • Python 中的树实现

    如何在Python中实现树 我是Python初学者 给我一个大概的想法 Build a Node类 具有一些内容对象和子对象列表 它们又是Node
  • 使用 ACL 的 Redis Docker 镜像

    我正在尝试测试新的 Redis 6 ACL 配置 我想使用尽可能简单的配置来运行测试以熟悉配置 我的 Redis 将作为 Docker 容器运行 请考虑一下我是一个 Redis 新手 My Dockerfile FROM redis 6 2
  • “旧版”和“稳定版”有什么区别?

    我正在使用 JQuery Mobile 有两个可用版本 稳定版本和旧版本 我对后一种不太熟悉 什么是旧版本 或者说和稳定版有什么区别 生产现场推荐使用哪一种 非常感谢 遗留版本是旧的稳定版本 仍然可用 并且可能受支持 因为有人可能需要它 例
  • 在 angularJS2 项目中哪里放置/找到 systemjs.config.js 文件?

    我是 Angular 2 的新手 并尝试在我的项目中使用 ng2 datetime picker 现在 在安装 ng2 datetime picker 包后 当我运行该项目时 得到了404 错误指出未找到 ng2 datetime pick
  • GWT 中的 Java 8 支持

    这似乎是一个非常基本的问题 但我到处搜索 发现几乎没有任何地方提到它 所以 我就在这里问一下 目前在 GWT 中支持 Java 8 新语言结构的计划是什么 此外 提议的 Java 8 库的哪些子集将用于客户端模拟 流 API 新的日期 时间
  • 在 C/C++ 中解析二进制消息流

    我正在编写二进制协议 Javad GRIL 协议 的解码器 它由大约一百条消息组成 数据格式如下 struct MsgData uint8 t num float x y z uint8 t elevation 这些字段是 ANSI 编码的