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

张开发
2026/4/13 18:47:08 15 分钟阅读

分享文章

【Socket消息传递详细版本】(3) 嵌入式设备间Socket通信传输图片 Client端函数
文章目录1 概要2 代码文件结构2.1 Client文件夹下函数介绍2.1.1 ImageClient.h2.1.2 ImageClient.cpp2.1.3 client_main.cpp2.1.4 client_main_test.cpp(博主自己按功能实现的文件)3 总结4 其余章节1 概要博主最近因为工程需求需要在两个嵌入式设备之间传输图片具体功能如下描述:硬件资源①米联客安路F3P-CZ02-FPSoc(FPGA) HOST端②rk3568 Client端满足功能①rk3568可以将消息包通过Socket接口发送给FPGA②FPGA通过解包消息报读取图片数据并存入相应的文件夹中2 代码文件结构整个工程代码分为三个结构ClientHostCommon其中Client文件夹内存放的为与Client端相关的函数Host文件夹内存放的为与Host端相关的函数Common文件夹内存放的为公共配置函数及部分工具包2.1 Client文件夹下函数介绍ClientClient文件夹下有4个文件client_main.cpp:用于快速测试的文件demo。client_main_test.cpp根据用户需求进行功能设计的文件其中对于封装类别的使用参考了client_main.cpp中的函数。ImageClient.h:ImageClient类别的头文件。ImageClient.cpp:ImageClient类别的源文件。2.1.1 ImageClient.h代码如下#pragmaonce#includecstdint#includestringclass ImageClient{public:ImageClient(std::string server_ip,uint16_tport);// image_name 可留空默认取 image_path 的文件名boolsendImage(conststd::stringmode,conststd::stringimage_path,conststd::stringimage_name_optstd::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、端口号和本地图片路径它会把图片文件读到内存里用自定义协议组装一个消息头MsgHeader包含 magic、版本号、mode 字符串长度、图片名长度、文件大小等建立 TCP 连接IPv4到指定服务器依次发送消息头 → mode 文本 → 文件名 → 图片二进制数据。代码如下#includeImageClient.h#includeImageTransferCommon.h#includearpa/inet.h#includenetinet/in.h#includesys/socket.h#includeunistd.h#includefilesystem#includefstream#includeiostream#includevectornamespace fsstd::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. 根据自定义协议填充消息头 MsgHeadermagic、版本号、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::stringmode,conststd::stringimage_path,conststd::stringimage_name_opt){// 1. 打开并读取图片文件到内存缓冲区std::ifstreamifs(image_path,std::ios::binary);if(!ifs.is_open()){std::cerrOpen file failed: image_path\n;returnfalse;}ifs.seekg(0,std::ios::end);std::streamoff szifs.tellg();ifs.seekg(0,std::ios::beg);if(sz0){std::cerrTell size failed: image_path\n;returnfalse;}std::vectorcharfilebuf(static_castsize_t(sz));ifs.read(filebuf.data(),sz);// 2. 确定要发送的文件名优先使用外部指定的 image_name_optstd::string nameimage_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::cerrmode/name too long.\n;returnfalse;}// 3. 创建 TCP socketintfd::socket(AF_INET,SOCK_STREAM,0);if(fd0){perror(socket);returnfalse;}// 4. 填写服务器地址结构体sockaddr_in addr{};addr.sin_familyAF_INET;addr.sin_porthtons(port_);if(::inet_pton(AF_INET,server_ip_.c_str(),addr.sin_addr)!1){std::cerrInvalid 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.versionhtons(VERSION);// 协议版本h.mode_lenhtons(static_castuint16_t(mode.size()));// mode 字符串长度h.name_lenhtons(static_castuint16_t(name.size()));// 文件名长度h.file_sizehtonll(static_castuint64_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::cerrsend failed.\n;::close(fd);returnfalse;}// 7. 关闭连接打印发送结果::close(fd);std::coutSent: modemode, namename, bytesfilebuf.size()\n;returntrue;}2.1.3 client_main.cpp测试文件demo:#includeImageClient.h#includeiostream#includestring#includeConfig.h/* 使用方式: ./client 192.168.1.10 5000 mode2_dataset1 /home/user/pics/dog.png dog-renamed.png */#defineCONF_PATH../Common/lan_image_transfer.confintmain(intargc,char*argv[]){if(argc5||argc6){std::cerrUsage: client server_ip port mode image_path [image_name]\n;return1;}std::string ipargv[1];uint16_tportstatic_castuint16_t(std::stoi(argv[2]));std::string modeargv[3];std::string pathargv[4];std::string name(argc6)?argv[5]:std::string();AppConfig cfg;std::string err;if(!loadConfig(CONF_PATH,cfg,err)){std::cerrLoad config failed: err\n;return1;}ImageClientcli(cfg.host_ip,cfg.port);bool okcli.sendImage(mode,path,name);returnok?0:2;}2.1.4 client_main_test.cpp(博主自己按功能实现的文件)RK3568接收到8种模式中的其中一个命令后将指定文件夹中的图片传输到远程的FPGA(Host)中。#includeImageClient.h#includeConfig.h#includeiostream#includestring#includefilesystem#includevector#includealgorithm#includecctype#defineCONF_PATH../../Common/lan_image_transfer.conf/* 发送的数据格式内容为模式图片数据图片名字 */namespace fsstd::filesystem;// 允许的 8 种模式staticboolisAllowedMode(conststd::stringm){staticconststd::vectorstd::stringmodes{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::pathp){if(!fs::is_regular_file(p))returnfalse;std::string extp.extension().string();std::transform(ext.begin(),ext.end(),ext.begin(),[](unsignedcharc){return(char)std::tolower(c);});staticconststd::vectorstd::stringexts{.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::cerrLoad config failed: err\n;return1;}if(cfg.host_ip.empty()||cfg.port0){std::cerrConfig 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;modetrim(mode);if(modequit||modeexit)break;if(modelist){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;foldertrim(folder);fs::pathdir(folder);if(!fs::exists(dir)||!fs::is_directory(dir)){std::cerr目录不存在或不是目录: dir\n;continue;}// 收集图片std::vectorfs::pathfiles;for(constautoentry: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_cnt0,fail_cnt0;for(constautof:files){conststd::string pathf.string();conststd::string namef.filename().string();// 用文件名作为远端保存名constbool okcli.sendImage(mode,path,name);if(ok)ok_cnt;elsefail_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端函数

更多文章