MessagePack:在C++中的使用及交叉编译在嵌入式Linux上的使用总结

一、MessagePack简介

科普一个冷门却强大的技术:MessagePack,简称msgpack。msgpack并非软件,而是一个标准,我们可以先把它看作二进制的JSON。提到“二进制JSON”,容易让人联想到更流行的标准BSON。如果你不了解BSON,可以自行查阅相关资料。总之,msgpack和BSON属于同类型的竞争产品,但在网络传输方面,msgpack无论是速度还是体积都完胜BSON。

JSON在序列化领域堪称神一般的存在。那什么是序列化呢?其实就是“降维打击”,任何多维的数据对象都必须被降维成一维,才能进行存储和网络传输。msgpack同样是一种序列化手段,只不过它序列化的结果是二进制格式,而非JSON的文本格式。这就如同HTTP 1.1进化到HTTP 2.0,从文本格式转变为二进制格式。
在这里插入图片描述

以一个JSON串为例,在UTF-8编码下每个字符占一个字节,总共27字节,转换成msgpack格式后仅剩下18字节,压缩了三分之一。这是因为JSON串本身存在许多无用信息,比如每个key的双引号可以省略,简单的布尔类型用四五个字节表示(true和false)。而XML的无用信息更多,这些都是文本格式本身的弊端,二进制格式则能解决这些问题,例如数字就应该用二进制表示。

msgpack对实数类型有一个特色,即可变长的实数类型,以至于128以下的正整数都可以用1个字节表示!如果说BSON编码在某些情况下比JSON还大,那么msgpack在任何情况下都比JSON小,最坏的情况如10以内的正整数,压缩率达100%,并且即使在这种情况下,msgpack的速度依然很快。

项目地址https://github.com/msgpack

官网地址https://msgpack.org/

二、在C++中使用MessagePack

1. 获取代码

在C++中使用MessagePack,我们可以从仓库地址:https://github.com/msgpack/msgpack-c 获取代码,然后切换到 cpp_master 分支:

git clone https://github.com/msgpack/msgpack-c
cd msgpack-c
git checkout cpp_master
2. 编译安装

使用以下命令进行编译安装:

cmake .. -DMSGPACK_USE_BOOST=OFF -DMSGPACK_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/root/arm_install
make install

这样,MessagePack就会被安装在 /root/arm_install 路径下。当然这个CMAKE_INSTALL_PREFIX可以指定为你想要的任何目录。

注意,这里并未指定交叉编译工具链,那么在嵌入式上可以使用吗?答案是的,因为messagepack都是头文件的形式提供的。嵌入式linux项目代码的编译则需要指定工具链。真正在嵌入式linux上使用请往下看。

3. 使用示例

以下是一个简单的使用示例:

#include <msgpack.hpp>
#include <string>
#include <iostream>
#include <sstream>

int main()
{
    // 创建一个包含整数、布尔值和字符串的元组
    msgpack::type::tuple<int, bool, std::string> src(1, true, "example");

    // 序列化对象到缓冲区
    std::stringstream buffer;
    msgpack::pack(buffer, src);

    // 模拟发送缓冲区数据
    buffer.seekg(0);

    // 从缓冲区反序列化数据到msgpack::object实例
    std::string str(buffer.str());
    msgpack::object_handle oh = msgpack::unpack(str.data(), str.size());

    // 获取反序列化后的对象
    msgpack::object deserialized = oh.get();

    // 输出反序列化后的对象
    std::cout << deserialized << std::endl;

    // 将msgpack::object实例转换回原始类型
    msgpack::type::tuple<int, bool, std::string> dst;
    deserialized.convert(dst);

    // 或者创建一个新的实例
    msgpack::type::tuple<int, bool, std::string> dst2 = deserialized.as<msgpack::type::tuple<int, bool, std::string>>();

    return 0;
}

测试demo的CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 3.5)
project(msgpack_example)

# 设置目标架构
#set(TARGET_ARCH "arm")

# 设置CMAKE_PREFIX_PATH以帮助CMake找到msgpack的安装路径
#set(CMAKE_PREFIX_PATH "/root/arm_install")

# 获取 ZeroMQ 库
#find_package(ZeroMQ REQUIRED)
#include_directories(${ZeroMQ_INCLUDE_DIRS})

# 获取 msgpack 库
find_package(msgpack-cxx REQUIRED)
#include_directories(${msgpack_INCLUDE_DIRS})

# 添加你的可执行文件
add_executable(msgpack_example main.cpp)

# 链接 ZeroMQ 库
#target_link_libraries(msgpack_example ${ZeroMQ_LIBRARIES})

# 链接 msgpack 库 重要
target_link_libraries(msgpack_example msgpack-cxx)

target_link_libraries(msgpack_example msgpack-cxx)重要,因为The library is header-only and target_link_libraries command just adds path to msgpack-c headers to your compiler’s include path.

If you do not use cmake, you can just add path yo msgpack-c headers to your include path:

g++ -I msgpack-c/include -I path_to_your_prj your_source_file.cpp

三、交叉编译在嵌入式Linux上的使用

1. 构建命令

执行以下命令进行交叉编译构建:
首先在项目的根目录下创建个build文件夹,然后进入build目录下执行。

#1
mkdir build
#2
cd build
#3
cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -DTARGET_ARCH=arm -DCMAKE_INSTALL_PREFIX=/root/arm_install -Dmsgpack-cxx_DIR=/root/arm_install/lib/cmake/msgpack-cxx
#最后,执行make开始编译
make

在这里插入图片描述

注意,这里需要确保 toolchain.cmake 文件配置正确,它包含了交叉编译所需的工具链信息。同时,msgpack-cxx_DIR 指定了 msgpack-cxx 的配置文件所在目录,以便CMake能够找到它。这个msgpack-cxx_DIR 也很关键,没配置它会导致你踩坑(cmake编译错误)。

注意上述示例中的/root/arm_install根据实际的情况,看上面安装在了具体哪个路径下。

上述如何不指定CMAKE_TOOLCHAIN_FILE,则会生成在宿主机linux系统下可运行的demo.

2. 注意事项
  • 工具链文件:toolchain.cmake 文件需要根据具体的嵌入式Linux平台进行配置,包括编译器路径、目标架构等信息。
  • 路径问题:确保 msgpack-cxx_DIR 指向的路径包含 msgpack-cxxConfig.cmakemsgpack-cxx-config.cmake 文件。
3.工具链toolchain.cmake文件示例

我的测试环境为: 正点原子的imx6ul开发板。

# 交叉编译工具链配置文件
# 用于嵌入式Linux系统和RISC-V MCU的交叉编译

# 设置系统名称
set(CMAKE_SYSTEM_NAME Linux)

# 设置处理器架构变量,可以通过命令行参数传入
# 例如: cmake -DTARGET_ARCH=arm ..
if(NOT DEFINED TARGET_ARCH)
    set(TARGET_ARCH "arm" CACHE STRING "Target architecture (arm or riscv)")
endif()

# 设置ARM工具链路径
set(ARM_TOOLCHAIN_PATH "/opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi")
# 设置RISC-V工具链路径
set(RISCV_TOOLCHAIN_PATH "/opt/tronlong/tina5.0_v1.0/rtos/lichee/rtos/tools/riscv64-elf-x86_64-20201104")

# 根据目标架构设置主工具链
if(${TARGET_ARCH} STREQUAL "arm")
    # ARM Linux工具链配置
    set(CMAKE_C_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-gcc)
    set(CMAKE_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-g++)
    set(CMAKE_FIND_ROOT_PATH /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi/)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    
    # 设置额外的编译标志
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)
    
    # 设置链接器
    set(CMAKE_LINKER ${CMAKE_C_COMPILER})
    set(CMAKE_AR ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ar)
    set(CMAKE_RANLIB ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ranlib)
    
elseif(${TARGET_ARCH} STREQUAL "riscv")
    # RISC-V工具链配置 (C906核心)
    set(CMAKE_C_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gcc)
    set(CMAKE_CXX_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-g++)
    set(CMAKE_FIND_ROOT_PATH ${RISCV_TOOLCHAIN_PATH}/riscv64-unknown-elf)
    set(CMAKE_SYSTEM_PROCESSOR riscv)
    
    # 设置RISC-V特定的编译标志 (C906核心)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)
    
    # 设置链接器
    set(CMAKE_LINKER ${CMAKE_C_COMPILER})
    set(CMAKE_AR ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ar)
    set(CMAKE_RANLIB ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ranlib)
    
else()
    message(FATAL_ERROR "不支持的目标架构: ${TARGET_ARCH}. 请使用 'arm' 或 'riscv'")
endif()

# 定义ARM工具链变量,供CMakeLists.txt使用
set(ARM_C_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-gcc)
set(ARM_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-g++)
set(ARM_AR ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-ar)
set(ARM_RANLIB ${ARM_TOOLCHAIN_PATH}/arm-linux-gnueabi-ranlib)
set(ARM_C_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon")
set(ARM_CXX_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=neon")

# 定义RISC-V工具链变量,供CMakeLists.txt使用
set(RISCV_C_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gcc)
set(RISCV_CXX_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-g++)
set(RISCV_AR ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ar)
set(RISCV_RANLIB ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ranlib)
set(RISCV_C_FLAGS "-march=rv64gcv0p7 -mabi=lp64d")
set(RISCV_CXX_FLAGS "-march=rv64gcv0p7 -mabi=lp64d")

# 设置查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# 禁用系统库路径
set(CMAKE_SKIP_RPATH TRUE)

# 设置交叉编译环境的库和头文件搜索路径
set(CMAKE_SYSROOT ${CMAKE_FIND_ROOT_PATH}) 

# 关键!!!,不能漏掉这个设置
set(CMAKE_FIND_ROOT_PATH /root/arm_install)

在这里插入图片描述

总结

通过以上步骤,我们就可以在C++项目中使用MessagePack进行数据序列化和反序列化,并且可以将其交叉编译到嵌入式Linux平台上。MessagePack的高性能和小体积特性使其在网络传输和嵌入式系统中具有很大的优势。

其他资源

MessagePack:最可能取代JSON的存在

https://github.com/msgpack/msgpack-c/tree/cpp_master

MessagePack - 简介及使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值