【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

相关推荐
我是koten5 分钟前
用Ansible查找文件并记录文件名的playbook
linux·运维·centos·ssh·ansible·find·playbook
云qq1 小时前
x86操作系统19——键盘驱动
linux·c语言·汇编
路溪非溪1 小时前
关于蓝牙技术的再补充
linux
爱宇阳2 小时前
Linux 安全加固:设置命令行无操作超时退出
linux·运维·安全
呆萌小新@渊洁2 小时前
声纹模型全流程实践-开发(训练 - 微调 - 部署 - 调用)
linux·服务器·python·语音识别
RisunJan2 小时前
Linux命令-getenforce命令(快速检查 Linux 系统中 SELinux 的当前运行模式)
linux·运维·服务器
SMF19192 小时前
解决在 Linux 系统中,当你尝试以 root 用户登录时遇到 “Access denied“ 的错误
java·linux·服务器
qq_479875432 小时前
systemd-resolved.service实验实战3
linux·服务器·c++
森焱森3 小时前
GD32F4 DSP
linux·c语言·arm开发·驱动开发·嵌入式硬件
天天进步20153 小时前
CentOS 实战:如何查看和分析信号量 (Semaphore) 的值
linux·运维·centos