【Socket消息传递】(1) 嵌入式设备间Socket通信传输图片

文章目录

  • [1 概要](#1 概要)
  • [2 工程介绍](#2 工程介绍)
    • [2.1 详细流程介绍](#2.1 详细流程介绍)
      • [2.1.1 消息结构](#2.1.1 消息结构)
    • [2.2 使用方法](#2.2 使用方法)
      • [2.2.1 Host端用法](#2.2.1 Host端用法)
      • [2.2.2 Client端用法](#2.2.2 Client端用法)
  • [3 总结](#3 总结)

1 概要

博主最近因为工程需求,需要在两个嵌入式设备之间传输图片,具体功能如下描述:

!!!(本篇论文主要类似于博主备忘录,因此写得不是很详细)

硬件资源:

①米联客安路F3P-CZ02-FPSoc(FPGA)

②rk3568

满足功能:

①rk3568可以将消息包通过Socket接口发送给FPGA

②FPGA通过解包消息报,读取图片数据,并存入相应的文件夹中

2 工程介绍

最近博主在完成一个工程,其内容为rk3568作为边缘设备进行目标检测,检测得到的图片上传FPGA并于FPGA的QT上进行可视化展示,因此整个流程就会涉及到多个设备之间的消息传输,最终选择Socket来进行消息传输,其中底层使用的是tcp协议。

2.1 详细流程介绍

客户端(RK3568)通过自定义二进制协议发送图像文件,FPGA按照检测模式(RK3568端的目标检测有8种模式,因此最终有8种模式的图)分目录保存到本地,这个服务器支持:

① 持续监听 TCP 端口

② 多线程处理客户端上传

③ 校验协议头

④ 校验模式是否允许

⑤ 清洗文件名

⑥ 按模式分类保存

⑦ 避免文件名冲突

⑧ 按文件大小安全接收字节流

也就是说Host的代码构建的为多线程文件接收服务器,专门用于存储图片或数据集文件。

2.1.1 消息结构

RK3568与FPGA之间图像传输使用Socket接口进行传输,其中为了保障数据的正确性,防止数据部分丢失导致数据损坏,使用了自定义的二进制协议格式,其具体结构如下:

消息头(16字节)\]\[模式字符串\]\[图像名称\]\[图像文件数据

消息头结构分为五个部分:

① magic (4字节):协议标识符

② version (2字节):协议版本号

③ mode_len (2字节):模式字符串长度(8个数据集,8中模式,每个模式的字符串名字不同)

④ name_len (2字节):图像名称长度

⑤ file_size (8字节):图像文件大小

整个传输流程分为如下步骤:

2.2 使用方法

整个工程分为三个部分,其中第一个部分为Client端(RK3568)的代码,第二个部分为Host端(FPGA)的代码,第三个部分Common中文件为共同属性配置文件夹,Host端和Client端代码会从其中读取相应的属性并配置实例化的客户端和服务器。

2.2.1 Host端用法

host端主要用于数据接收后解包,并存储图片,其用法如下:

编译得到可执行文件host,之后

c 复制代码
./host

即可持续监听Common文件夹中lan_image_transfer.conf中指定的端口,其中

lan_image_transfer.conf如下:

c 复制代码
# LAN Image Transfer 配置
# 注:以 '#' 或 ';' 开头的行是注释

[network]
# client 侧将连接到这个 IP
host_ip = 127.0.0.1
# host 监听端口,同时 client 也用该端口进行连接
port    = 8899

base_dir = ./images

Host侧的核心代码如下:进行消息头检验与消息包解包

c 复制代码
void ImageServer::handleClient(int cfd)
{
    MsgHeader h{};
    if (!recv_all(cfd, &h, sizeof(h)))
    {
        std::cerr << "recv header failed\n";
        ::close(cfd);
        return;
    }
    if (std::memcmp(h.magic, MAGIC, 4) != 0 || ntohs(h.version) != VERSION)
    {
        std::cerr << "bad magic/version\n";
        ::close(cfd);
        return;
    }

    uint16_t mode_len = ntohs(h.mode_len);
    uint16_t name_len = ntohs(h.name_len);
    uint64_t file_sz = ntohll(h.file_size);

    if (mode_len == 0 || name_len == 0)
    {
        std::cerr << "invalid lengths\n";
        ::close(cfd);
        return;
    }

    std::string mode(mode_len, '\0');
    std::string name(name_len, '\0');

    if (!recv_all(cfd, mode.data(), mode_len) ||
        !recv_all(cfd, name.data(), name_len))
    {
        std::cerr << "recv strings failed\n";
        ::close(cfd);
        return;
    }

    // ------ 新增:仅允许 8 种模式(如不想限制,可删除此段)------
    if (!isAllowedMode(mode))
    {
        std::cerr << "reject unknown mode: " << mode << "\n";
        ::close(cfd);
        return;
    }

    std::string mode_safe = sanitize(mode);
    std::string name_safe = sanitize(name);

    std::filesystem::path dir = base_dir_ / (mode_safe + "_folder");
    std::error_code ec;
    fs::create_directories(dir, ec);

    fs::path outfile = uniquePathIfExists(dir / name_safe);

    std::ofstream ofs(outfile, std::ios::binary);
    if (!ofs.is_open())
    {
        std::cerr << "open output failed: " << outfile << "\n";
        ::close(cfd);
        return;
    }

    const size_t BUF = 1 << 16;
    std::vector<char> buf(BUF);
    uint64_t remain = file_sz;

    while (remain > 0)
    {
        size_t chunk = static_cast<size_t>(std::min<uint64_t>(remain, BUF));
        if (!recv_all(cfd, buf.data(), chunk))
        {
            std::cerr << "recv file chunk failed\n";
            ::close(cfd);
            return;
        }
        ofs.write(buf.data(), chunk);
        remain -= chunk;
    }

    ofs.close();
    ::close(cfd);

    std::cout << "Saved: mode=" << mode_safe
              << ", name=" << outfile.filename().string()
              << ", bytes=" << file_sz
              << ", path=" << outfile << "\n";
}

2.2.2 Client端用法

Client端会根据lan_image_transfer.conf中指定的ip地址建立连接,并向其ip地址发送消息,lan_image_transfer.conf如下(和Host中一致):

c 复制代码
# LAN Image Transfer 配置
# 注:以 '#' 或 ';' 开头的行是注释

[network]
# client 侧将连接到这个 IP
host_ip = 127.0.0.1
# host 监听端口,同时 client 也用该端口进行连接
port    = 8899

base_dir = ./images

编译出可执行文件client后执行如下命令即可:

c 复制代码
./client

之后终端需要提供两个输入,一个为图像检测模式,一个为将要被传输图片的文件夹,之后即可进行消息传输,Host端接收文件后会根据检测模式创立文件夹,并根据图像名字将图像存储到此文件夹中。

3 总结

讲述了如何在两个设备之间传输图像数据。

资源下载链接 https://download.csdn.net/download/qq_54050349/92350483

相关推荐
有梦想有行动15 分钟前
ClickHouse的Partition和Part概念
linux·数据库·clickhouse
物理与数学18 分钟前
linux内核 Multi-Gen LRU 算法
linux·linux内核
强风7941 小时前
Linux-线程的同步与互斥
linux·服务器
提伯斯6461 小时前
Orangepi R1内置了哪些网卡驱动?(全志H3的板子)
linux·网络·wifi·全志h3
技术摆渡人1 小时前
专题二:【驱动进阶】打破 Linux 驱动开发的黑盒:从 GPIO 模拟到 DMA 陷阱全书
android·linux·驱动开发
wishchin1 小时前
Jetson Orin Trt: No CMAKE_CUDA_COMPILER could be found
linux·运维·深度学习
ArrebolJiuZhou1 小时前
03 rtp,rtcp,sdp的包结构
linux·运维·服务器·网络·arm开发
403240731 小时前
Ubuntu/Jetson 通用:NVMe 硬盘分区、挂载及开机自动挂载完整教程
linux·运维·ubuntu
田地和代码2 小时前
linux应用用户安装jdk以后 如果root安装hbase客户端需要jdk还需要再次安装吗
java·linux·hbase
乔碧萝成都分萝2 小时前
二十四、Linux如何处理中断
linux·驱动开发·嵌入式