news 2026/6/22 14:26:15

【Socket消息传递详细版本】(3) 嵌入式设备间Socket通信传输图片 Client端函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Socket消息传递详细版本】(3) 嵌入式设备间Socket通信传输图片 Client端函数

文章目录

  • 1 概要
  • 2 代码文件结构
    • 2.1 Client文件夹下函数介绍
      • 2.1.1 ImageClient.h
      • 2.1.2 ImageClient.cpp
      • 2.1.3 client_main.cpp
      • 2.1.4 client_main_test.cpp(博主自己按功能实现的文件)
  • 3 总结
  • 4 其余章节

1 概要

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

硬件资源:
①米联客安路F3P-CZ02-FPSoc(FPGA) (HOST端)
②rk3568 (Client端)

满足功能:
①rk3568可以将消息包通过Socket接口发送给FPGA
②FPGA通过解包消息报,读取图片数据,并存入相应的文件夹中

2 代码文件结构


整个工程代码分为三个结构,Client,Host,Common,其中:

  1. Client文件夹内存放的为与Client端相关的函数
  2. Host文件夹内存放的为与Host端相关的函数
  3. Common文件夹内存放的为公共配置函数及部分工具包

2.1 Client文件夹下函数介绍

ClientClient文件夹下有4个文件:

  1. client_main.cpp:
    用于快速测试的文件demo。
  2. client_main_test.cpp:
    根据用户需求进行功能设计的文件,其中对于封装类别的使用参考了client_main.cpp中的函数。
  3. ImageClient.h:
    ImageClient类别的头文件。
  4. ImageClient.cpp:
    ImageClient类别的源文件。

2.1.1 ImageClient.h

代码如下:

#pragmaonce#include<cstdint>#include<string>class ImageClient{public:ImageClient(std::string server_ip,uint16_tport);// image_name 可留空,默认取 image_path 的文件名boolsendImage(conststd::string&mode,conststd::string&image_path,conststd::string&image_name_opt=std::string());private:std::string server_ip_;uint16_tport_;};

其中封装了两个函数,一个是初始化时会自动调用的ImageClient函数,一个是发送数据使用的sendImage函数,其中需要传递参数为检测模式mode, 发送图片的图片路径image_path, 图片的名字image_name_opt。

私有变量为:
远端的ip:server_ip_,远端的监听端口:port_。

2.1.2 ImageClient.cpp

整体上是在实现一个图片上传客户端
给它一个服务器 IP、端口号和本地图片路径,它会:

  1. 把图片文件读到内存里
  2. 用自定义协议组装一个消息头(MsgHeader),包含 magic、版本号、mode 字符串长度、图片名长度、文件大小等;
  3. 建立 TCP 连接(IPv4)到指定服务器;
  4. 依次发送:消息头 → mode 文本 → 文件名 → 图片二进制数据。
    代码如下:
#include"ImageClient.h"#include"ImageTransferCommon.h"#include<arpa/inet.h>#include<netinet/in.h>#include<sys/socket.h>#include<unistd.h>#include<filesystem>#include<fstream>#include<iostream>#include<vector>namespace fs=std::filesystem;using namespace itx;/** * @brief 构造函数 * * 保存目标服务器的 IP 和端口,用于后续建立 TCP 连接进行图片传输。 * * @param server_ip 服务器 IPv4 地址字符串,例如 "127.0.0.1" * @param port 服务器监听端口号 */ImageClient::ImageClient(std::string server_ip,uint16_tport):server_ip_(std::move(server_ip)),port_(port){}/** * @brief 发送一张图片到服务器 * * 1. 从本地读取指定路径的图片文件到内存; * 2. 根据自定义协议填充消息头 MsgHeader(magic、版本号、mode 长度、文件名长度、文件大小等); * 3. 建立 TCP 连接到构造函数中指定的 server_ip_ 和 port_; * 4. 依次发送:消息头 → mode 字符串 → 图片名 → 图片二进制数据; * 5. 发送完成后关闭 socket。 * * @param mode 业务模式字符串(例如 "upload"、"classify" 等),由服务器按约定解析 * @param image_path 本地图片文件路径 * @param image_name_opt 可选的图片名;如果为空,则使用 image_path 中的文件名部分 * @return true 发送成功 * @return false 发送失败(过程中会在标准错误输出错误信息) */bool ImageClient::sendImage(conststd::string&mode,conststd::string&image_path,conststd::string&image_name_opt){// 1. 打开并读取图片文件到内存缓冲区std::ifstreamifs(image_path,std::ios::binary);if(!ifs.is_open()){std::cerr<<"Open file failed: "<<image_path<<"\n";returnfalse;}ifs.seekg(0,std::ios::end);std::streamoff sz=ifs.tellg();ifs.seekg(0,std::ios::beg);if(sz<0){std::cerr<<"Tell size failed: "<<image_path<<"\n";returnfalse;}std::vector<char>filebuf(static_cast<size_t>(sz));ifs.read(filebuf.data(),sz);// 2. 确定要发送的文件名(优先使用外部指定的 image_name_opt)std::string name=image_name_opt.empty()?fs::path(image_path).filename().string():image_name_opt;// mode 和 name 限制在 uint16_t 能表示的范围内(协议字段是 16 位长度)if(mode.size()>65535||name.size()>65535){std::cerr<<"mode/name too long.\n";returnfalse;}// 3. 创建 TCP socketintfd=::socket(AF_INET,SOCK_STREAM,0);if(fd<0){perror("socket");returnfalse;}// 4. 填写服务器地址结构体sockaddr_in addr{};addr.sin_family=AF_INET;addr.sin_port=htons(port_);if(::inet_pton(AF_INET,server_ip_.c_str(),&addr.sin_addr)!=1){std::cerr<<"Invalid server IP: "<<server_ip_<<"\n";::close(fd);returnfalse;}// 5. 连接到服务器if(::connect(fd,(sockaddr*)&addr,sizeof(addr))<0){perror("connect");::close(fd);returnfalse;}// 6. 构造并发送消息头 + 文本字段 + 文件数据MsgHeader h{};std::memcpy(h.magic,MAGIC,4);// 魔数标识协议h.version=htons(VERSION);// 协议版本h.mode_len=htons(static_cast<uint16_t>(mode.size()));// mode 字符串长度h.name_len=htons(static_cast<uint16_t>(name.size()));// 文件名长度h.file_size=htonll(static_cast<uint64_t>(filebuf.size()));// 文件大小(字节数)// 依次发送:头 → mode → name → 文件数据if(!send_all(fd,&h,sizeof(h))||!send_all(fd,mode.data(),mode.size())||!send_all(fd,name.data(),name.size())||!send_all(fd,filebuf.data(),filebuf.size())){std::cerr<<"send failed.\n";::close(fd);returnfalse;}// 7. 关闭连接,打印发送结果::close(fd);std::cout<<"Sent: mode="<<mode<<", name="<<name<<", bytes="<<filebuf.size()<<"\n";returntrue;}

2.1.3 client_main.cpp

测试文件demo:

#include"ImageClient.h"#include<iostream>#include<string>#include"Config.h"/* 使用方式: ./client 192.168.1.10 5000 mode2_dataset1 /home/user/pics/dog.png dog-renamed.png */#defineCONF_PATH"../Common/lan_image_transfer.conf"intmain(intargc,char*argv[]){if(argc<5||argc>6){std::cerr<<"Usage: client <server_ip> <port> <mode> <image_path> [image_name]\n";return1;}std::string ip=argv[1];uint16_tport=static_cast<uint16_t>(std::stoi(argv[2]));std::string mode=argv[3];std::string path=argv[4];std::string name=(argc==6)?argv[5]:std::string();AppConfig cfg;std::string err;if(!loadConfig(CONF_PATH,cfg,&err)){std::cerr<<"Load config failed: "<<err<<"\n";return1;}ImageClientcli(cfg.host_ip,cfg.port);bool ok=cli.sendImage(mode,path,name);returnok?0:2;}

2.1.4 client_main_test.cpp(博主自己按功能实现的文件)

RK3568接收到8种模式中的其中一个命令后将指定文件夹中的图片传输到远程的FPGA(Host)中。

#include"ImageClient.h"#include"Config.h"#include<iostream>#include<string>#include<filesystem>#include<vector>#include<algorithm>#include<cctype>#defineCONF_PATH"../../Common/lan_image_transfer.conf"/* 发送的数据格式内容为:模式,图片数据,图片名字 */namespace fs=std::filesystem;// 允许的 8 种模式staticboolisAllowedMode(conststd::string&m){staticconststd::vector<std::string>modes={"mode1_picture1","mode1_picture2","mode1_picture3","mode1_picture4","mode2_dataset1","mode2_dataset2","mode3_dataset1","mode3_dataset2"};returnstd::find(modes.begin(),modes.end(),m)!=modes.end();}// 简单判断是否图片文件staticboolisImageFile(constfs::path&p){if(!fs::is_regular_file(p))returnfalse;std::string ext=p.extension().string();std::transform(ext.begin(),ext.end(),ext.begin(),[](unsignedcharc){return(char)std::tolower(c);});staticconststd::vector<std::string>exts={".jpg",".jpeg",".png",".bmp",".tif",".tiff",".gif",".webp"};returnstd::find(exts.begin(),exts.end(),ext)!=exts.end();}// 去掉前后空白staticstd::stringtrim(std::string s){autoissp=[](unsignedcharc){returnstd::isspace(c);};s.erase(s.begin(),std::find_if(s.begin(),s.end(),[&](unsignedcharc){return!issp(c);}));s.erase(std::find_if(s.rbegin(),s.rend(),[&](unsignedcharc){return!issp(c);}).base(),s.end());returns;}intmain(intargc,char*argv[]){// 读取配置AppConfig cfg;std::string err;if(!loadConfig(CONF_PATH,cfg,&err)){std::cerr<<"Load config failed: "<<err<<"\n";return1;}if(cfg.host_ip.empty()||cfg.port==0){std::cerr<<"Config missing 'host_ip' or 'port'\n";return1;}ImageClientcli(cfg.host_ip,cfg.port);std::cout<<"目标主机: "<<cfg.host_ip<<":"<<cfg.port<<"\n";std::cout<<"输入 'list' 查看可用模式,输入 'quit' 或 'exit' 退出。\n";while(true){// 读模式std::string mode;std::cout<<"\n请输入模式: ";if(!std::getline(std::cin,mode))break;mode=trim(mode);if(mode=="quit"||mode=="exit")break;if(mode=="list"){std::cout<<"可用模式: ""\n 1.mode1_picture1 \n 2.mode1_picture2 \n 3.mode1_picture3 \n 4.mode1_picture4 \n"" 5.mode2_dataset1 \n 6.mode2_dataset2 \n 7.mode3_dataset1 \n 8.mode3_dataset2 \n";continue;}if(!isAllowedMode(mode)){std::cerr<<"模式无效,请重试(输入 'list' 查看可用模式)。\n";continue;}// 读文件夹std::string folder;std::cout<<"请输入图片文件夹路径: ";if(!std::getline(std::cin,folder))break;folder=trim(folder);fs::pathdir(folder);if(!fs::exists(dir)||!fs::is_directory(dir)){std::cerr<<"目录不存在或不是目录: "<<dir<<"\n";continue;}// 收集图片std::vector<fs::path>files;for(constauto&entry:fs::directory_iterator(dir)){if(isImageFile(entry.path()))files.push_back(entry.path());}std::sort(files.begin(),files.end());if(files.empty()){std::cout<<"该目录未发现图片文件。\n";continue;}std::cout<<"发现 "<<files.size()<<" 个图片文件,开始发送...\n";size_tok_cnt=0,fail_cnt=0;for(constauto&f:files){conststd::string path=f.string();conststd::string name=f.filename().string();// 用文件名作为远端保存名constbool ok=cli.sendImage(mode,path,name);if(ok)++ok_cnt;else++fail_cnt;}std::cout<<"发送完成:成功 "<<ok_cnt<<" 个,失败 "<<fail_cnt<<" 个。\n";// 回到循环,可继续输入下一个模式与文件夹}std::cout<<"已退出。\n";return0;}

3 总结

本章节介绍了嵌入式设备间Socket通信传输图片 Host端函数。

4 其余章节

【Socket消息传递】(1) 嵌入式设备间Socket通信传输图片
【Socket消息传递详细版本】(2) 嵌入式设备间Socket通信传输图片 Common公共函数
【Socket消息传递详细版本】(3) 嵌入式设备间Socket通信传输图片 Client端函数
【Socket消息传递详细版本】(4) 嵌入式设备间Socket通信传输图片 Host端函数

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 14:25:39

AI让老照片说话:Super Resolution与人脸增强联合实战

AI让老照片说话&#xff1a;Super Resolution与人脸增强联合实战 1. 项目简介 你有没有翻出过老照片&#xff0c;却发现画面模糊、细节丢失&#xff0c;根本看不清人物的表情&#xff1f;或者从网上下载的图片分辨率太低&#xff0c;放大后全是马赛克&#xff1f; 现在&…

作者头像 李华
网站建设 2026/4/30 10:56:20

如何用缠论可视化分析插件快速识别股票买卖点

如何用缠论可视化分析插件快速识别股票买卖点 【免费下载链接】Indicator 通达信缠论可视化分析插件 项目地址: https://gitcode.com/gh_mirrors/ind/Indicator 缠论可视化分析插件是一款专为通达信软件设计的缠论技术指标插件&#xff0c;能够自动识别K线图中的线段、中…

作者头像 李华
网站建设 2026/6/3 13:34:53

大疆L1任务文件全解析:从原始数据到可处理格式的转换指南

1. 大疆L1任务文件格式初探 第一次拿到大疆L1的飞行数据时&#xff0c;我完全被那一堆文件扩展名搞懵了。CLC、CLI、LDR、RTB...这些看起来像密码一样的文件&#xff0c;其实每个都承载着不同的关键数据。经过多次项目实战&#xff0c;我终于摸清了它们的门道。 L1作为大疆首款…

作者头像 李华
网站建设 2026/4/13 18:42:58

多语言实战:5种编程语言调用免费天气预报API全解析

1. 天气预报API的实用价值与技术选型 天气预报API对于开发者来说就像是一把瑞士军刀&#xff0c;看似简单却能解决各种实际问题。我去年给本地一家生鲜配送公司做系统时&#xff0c;就用天气数据优化了他们的配送路线&#xff0c;暴雨天自动避开易积水路段&#xff0c;直接帮他…

作者头像 李华
网站建设 2026/4/13 18:42:42

Ostrakon-VL像素终端部署:飞桨PaddlePaddle后端兼容方案

Ostrakon-VL像素终端部署&#xff1a;飞桨PaddlePaddle后端兼容方案 1. 项目背景与特点 1.1 像素特工终端概述 Ostrakon-VL像素终端是一款专为零售与餐饮行业设计的智能扫描工具&#xff0c;基于Ostrakon-VL-8B多模态大模型开发。与传统工业级UI不同&#xff0c;该终端采用8…

作者头像 李华
网站建设 2026/4/13 18:40:40

开了8小时会却毫无产出?把你的人生当成“带宽分配”

你的一天&#xff0c;看起来很满&#xff0c;其实很空老马今天想跟你聊聊“忙”这事儿。上周五晚上七点多&#xff0c;我一个还在大厂做中层的老同事给我发了条微信。“老马&#xff0c;我快疯了。今天从早到晚开了五个会&#xff0c;回了两百多条微信&#xff0c;中间还帮隔壁…

作者头像 李华