文章目录
- [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