协议定制 + Json序列化反序列化
文章目录
- 协议定制 + Json序列化反序列化
- 1. 再谈 "协议"
- 1.1 结构化数据
- 1.2 序列化和反序列化
- 2. 网络版计算器
- 2.1 服务端
- 2.2 协议定制
- (1) 网络发送和读取的正确理解
- (2) 协议定制的问题
- 2.3 客户端
- 2.4 代码
- 3. Json实现序列化反序列化
- 3.1 简单介绍
- 3.2 使用
协议定制 + Json序列化反序列化
1. 再谈 “协议”
1.1 结构化数据
协议是一种 “约定”,socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些"结构化的数据" 怎么办呢?
结构化数据:
比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身一起发送给别人,这种一起发送的就是结构化数据。
1.2 序列化和反序列化
- 序列化:就是将对象转化成字节序列的过程。便于在传递和保存对象时保证对象的完整性和可传递性同时对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
- 反序列化:就是将字节序列转化成对象的过程。便于根据字节流中保存的对象状态及描述信息,通过反序列化重建对象。
-
在socket编程的基础上,我们发现在实际生活中网络通信并不是单单发送一条消息本身,它包含了很多其他类型的数据,所以我们引入了结构化数据的概念,将这些各种类型的数据都定义在一个结构体中形成结构化数据方便被上层设置与读取。
-
发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体;实现序列化和反序列化方便网络通信。
-
那么发送方发送的结构化数据序列化成字符串,接收方收到后是怎么知道反序列化成结构化数据呢?这是因为两者间存在定址好的协议。所以协议的本质就是双方约定好的某种格式的数据,常见的就是用结构体或者类来表达。
2. 网络版计算器
我们需要实现一个服务器版的计算器,客户端把要计算的两个数和计算类型发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。
2.1 服务端
服务端创建步骤:
- 调用socket,创建套接字
- 调用bind,绑定端口
- 调用listen,将套接字状态设置为监听
- 调用accept,获取新连接
- 处理读取与写入的问题(重点)
2.2 协议定制
(1) 网络发送和读取的正确理解
客户端和服务器通信时,会调用read和write函数,它们是把数据直接发送到对端吗?不是
- TCP协议有自己的发送缓冲区和接收缓冲区
- 调用write本质:把用户所对应的数据拷贝到TCP的发送缓冲区
- 调用read本质:把数据从接收缓冲区拷贝到用户层
- 所以read和write的本质是拷贝函数
- 把数据拷贝到TCP发送缓冲区后,剩下的数据怎么发,是由TCP决定的,所以TCP又叫做传输控制协议
- 因为发送和接收是成对的,可以同时进行的,所以TCP协议是全双工的
综上:
- TCP通信的本质是把自己发送缓冲区的数据经过网络拷贝到对方的接收缓冲区中
- 网络通信的本质也是拷贝
(2) 协议定制的问题
在定制协议之前先解决一个问题,之前在使用TCP协议时我们只是简单的读取,没有考虑TCP是面向字节流的,读取数据不完整的问题。这里同样存在相同的问题,如果一下子对方发送了很多报文,这些报文都堆积在TCP的接收缓冲区中,你怎么保证自己读到的是一个完整的报文呢?
我们采用这样的方式:
- 对报文定长
- 使用特殊符号(在报文与报文之间增加特殊符号)
- 自描述方式(自己设计协议)
协议设计格式:
Protocol.hpp
#include<string>
#include<iostream>
#include<vector>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.hpp"
using namespace std;// 给网络版本计算器定制协议
namespace Protocol_ns
{#define SEP " "#define SEP_LEN strlen(SEP) // 绝对不能写成sizeof#define HEADER_SEP "\r\n"#define HEADER_SEP_LEN strlen("\r\n")// "长度"\r\n" "_x op _y"\r\n// "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷// 请求/响应 = 报头\r\n有效载荷\r\n// 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n// "10 + 20" => "7"r\n""10 + 20"\r\nstring AddHeader(string&str){cout<<"AddHeader 之前:\n"<<str<<endl;string s=to_string(str.size());s+=HEADER_SEP;s+=str;s+=HEADER_SEP;cout<<"AddHeader 之后:\n"<<s<<endl;return s;}// "7"r\n""10 + 20"\r\n => "10 + 20" string RemoveHeader(const string&str,int len){cout<<"RemoveHeader 之前:\n"<<str<<endl;// 从后面开始截取string res=str.substr(str.size()-HEADER_SEP_LEN-len,len); cout<<"RemoveHeader 之后:\n"<<res<<endl; return res;}int Readpackage(int sock,string&inbuffer,string*package){cout<<"ReadPackage inbuffer 之前:\n"<<inbuffer<<endl;// 边读取char buffer[1024];ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);if(s<=0)return -1;buffer[s]=0;inbuffer+=buffer;cout<<"ReadPackage inbuffer 之中:\n"<<inbuffer<<endl;// 边分析, "7"r\n""10 + 20"\r\nauto pos=inbuffer.find(HEADER_SEP);if(pos==string::npos)return 0;string lenStr=inbuffer.substr(0,pos); // 获取头部字符串, 没有动inbufferint len=Util::toInt(lenStr); // 得到有效载荷的长度 => "123" -> 123int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size(); // 得到整个报文长度if(inbuffer.size()<targetPackageLen) // 不是一个完整的报文return 0;*package=inbuffer.substr(0,targetPackageLen); // 提取到了报文有效载荷, 没有动inbufferinbuffer.erase(0,targetPackageLen); // 从inbuffer中直接移除整个报文cout<<"ReadPackage inbuffer 之后:\n"<<inbuffer<<endl;return len;}// Request && Response都要提供序列化和反序列化功能// 1. 自己手写// 2. 用别人的 --- json, xml, protobufclass Request{public:Request(){}Request(int x,int y,char op):_x(x),_y(y),_op(op){}// 序列化: struct->stringbool Serialize(string* outStr) {*outStr=""; string x_string=to_string(_x);string y_string=to_string(_y);// 手动序列化*outStr=x_string + SEP + _op + SEP + y_string;std::cout << "Request Serialize:\n"<< *outStr << std::endl;return true;}// 反序列化: string->structbool Deserialize(const string&inStr) {// inStr: 10 + 20 => [0]=>10, [1]=>+, [2]=>20vector<string> result;Util::StringSplit(inStr,SEP,&result);if(result.size()!=3)return false;if(result[1].size()!=1)return false;_x=Util::toInt(result[0]);_y=Util::toInt(result[2]);_op=result[1][0];return true;}~Request(){}public:// _x op _y ==> 10 * 9 ? ==> 10 / 0 ?int _x;int _y;char _op;};class Response{public:Response(){}Response(int result,int code):_result(result),_code(code){}// 序列化: struct->stringbool Serialize(string* outStr) {// _result _code*outStr=""; string res_string = to_string(_result);string code_string = to_string(_code);// 手动序列化*outStr=res_string + SEP + code_string;return true;}// 反序列化: string->structbool Deserialize(const string&inStr) {// 10 0vector<string> result;Util::StringSplit(inStr,SEP,&result);if(result.size()!=2)return false;_result=Util::toInt(result[0]);_code=Util::toInt(result[1]);return true;}~Response(){}public:int _result;int _code; // 0 success; 1,2,3,4代表不同错误码};}
Util.hpp
#pragma once#include<iostream>
#include<string>
#include<vector>
using namespace std;class Util
{
public:// 输入: const &// 输出: *// 输入输出: *static bool StringSplit(const string &str, const string &sep, vector<string> *result){// 10 + 20size_t start = 0;while (start < str.size()){auto pos = str.find(sep, start);if (pos == string::npos)break;result->push_back(str.substr(start, pos - start));// 更新位置start = pos + sep.size();}// 处理最后的字符串if(start<str.size())result->push_back(str.substr(start));return true;}static int toInt(const string&s) // 字符串转整数{return atoi(s.c_str());}
};
2.3 客户端
客户端创建步骤:
- 调用socket,创建套接字
- 客户端不用自己bind端口
- 调用connect,连接服务器
- 处理读取与写入的问题
2.4 代码
完整的代码:lesson36 · 遇健/Linux - 码云 - 开源中国 (gitee.com)
运行结果:
3. Json实现序列化反序列化
3.1 简单介绍
上面是自己定制协议实现序列化和反序列化,下面我们使用一些现成的方案来实现序列化和反序列化。C++常用的:protobuf 和 json,这里使用简单的 json。
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。Json数据由键值对组成,大括号表示对象,方括号表示数组。
3.2 使用
- 安装json库
yum install -y jsoncpp-devel
- 使用json包含的头文件:
#include <jsoncpp/json/json.h>
注意makefile文件要包含Json库的名称
我们在使用的时候直接创建Json对象来进行序列化和反序列化
Protocol.hpp
#include<string>
#include<iostream>
#include<vector>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.hpp"
#include<jsoncpp/json/json.h>
using namespace std;// #define MYSELF 1// 给网络版本计算器定制协议namespace Protocol_ns
{#define SEP " "#define SEP_LEN strlen(SEP) // 绝对不能写成sizeof#define HEADER_SEP "\r\n"#define HEADER_SEP_LEN strlen("\r\n")// "长度"\r\n" "_x op _y"\r\n// "10 + 20" => "7"r\n""10 + 20"\r\n => 报头 + 有效载荷// 请求/响应 = 报头\r\n有效载荷\r\n// 请求 = 报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n报头\r\n有效载荷\r\n// 未来: "长度"\r\n"协议号\r\n""_x op _y"\r\n// "10 + 20" => "7"r\n""10 + 20"\r\nstring AddHeader(string&str){cout<<"AddHeader 之前:\n"<<str<<endl;string s=to_string(str.size());s+=HEADER_SEP;s+=str;s+=HEADER_SEP;cout<<"AddHeader 之后:\n"<<s<<endl;return s;}// "7"r\n""10 + 20"\r\n => "10 + 20" string RemoveHeader(const string&str,int len){cout<<"RemoveHeader 之前:\n"<<str<<endl;// 从后面开始截取string res=str.substr(str.size()-HEADER_SEP_LEN-len,len); cout<<"RemoveHeader 之后:\n"<<res<<endl; return res;}int Readpackage(int sock,string&inbuffer,string*package){cout<<"ReadPackage inbuffer 之前:\n"<<inbuffer<<endl;// 边读取char buffer[1024];ssize_t s=recv(sock,&buffer,sizeof(buffer)-1,0);if(s<=0)return -1;buffer[s]=0;inbuffer+=buffer;cout<<"ReadPackage inbuffer 之中:\n"<<inbuffer<<endl;// 边分析, "7"r\n""10 + 20"\r\nauto pos=inbuffer.find(HEADER_SEP);if(pos==string::npos)return 0;string lenStr=inbuffer.substr(0,pos); // 获取头部字符串, 没有动inbufferint len=Util::toInt(lenStr); // 得到有效载荷的长度 => "123" -> 123int targetPackageLen=len+2*HEADER_SEP_LEN+lenStr.size(); // 得到整个报文长度if(inbuffer.size()<targetPackageLen) // 不是一个完整的报文return 0;*package=inbuffer.substr(0,targetPackageLen); // 提取到了报文有效载荷, 没有动inbufferinbuffer.erase(0,targetPackageLen); // 从inbuffer中直接移除整个报文cout<<"ReadPackage inbuffer 之后:\n"<<inbuffer<<endl;return len;}// Request && Response都要提供序列化和反序列化功能// 1. 自己手写// 2. 用别人的class Request{public:Request(){}Request(int x,int y,char op):_x(x),_y(y),_op(op){}// 序列化: struct->stringbool Serialize(string* outStr) {*outStr="";
#ifdef MYSELFstring x_string=to_string(_x);string y_string=to_string(_y);// 手动序列化*outStr=x_string + SEP + _op + SEP + y_string;std::cout << "Request Serialize:\n"<< *outStr << std::endl;
#elseJson::Value root; // Value: 一种万能对象, 接受任意的kv类型root["x"]=_x;root["y"]=_y;root["op"]=_op;// Json::FastWriter writer; // writer: 是用来进行序列化的 struct -> stringJson::StyledWriter writer;*outStr=writer.write(root);
#endifreturn true;}// 反序列化: string->structbool Deserialize(const string&inStr) {
#ifdef MYSELF// inStr: 10 + 20 => [0]=>10, [1]=>+, [2]=>20vector<string> result;Util::StringSplit(inStr,SEP,&result);if(result.size()!=3)return false;if(result[1].size()!=1)return false;_x=Util::toInt(result[0]);_y=Util::toInt(result[2]);_op=result[1][0];#elseJson::Value root; Json::Reader reader; // Reader: 是用来反序列化的reader.parse(inStr,root);_x=root["x"].asUInt();_y=root["y"].asUInt();_op=root["op"].asUInt();#endifPrint();return true;}void Print(){std::cout << "_x: " << _x << std::endl;std::cout << "_y: " << _y << std::endl;std::cout << "_z: " << _op << std::endl;}~Request(){}public:// _x op _y ==> 10 * 9 ? ==> 10 / 0 ?int _x;int _y;char _op;};class Response{public:Response(){}Response(int result,int code):_result(result),_code(code){}// 序列化: struct->stringbool Serialize(string* outStr) {// _result _code*outStr="";
#ifdef MYSELFstring res_string = to_string(_result);string code_string = to_string(_code);// 手动序列化*outStr=res_string + SEP + code_string;#elseJson::Value root; root["result"]=_result;root["code"]=_code;// Json::FastWriter writer;Json::StyledWriter writer;*outStr=writer.write(root);
#endifreturn true;}// 反序列化: string->structbool Deserialize(const string&inStr) {
#ifdef MYSELF// 10 0vector<string> result;Util::StringSplit(inStr,SEP,&result);if(result.size()!=2)return false;_result=Util::toInt(result[0]);_code=Util::toInt(result[1]);#elseJson::Value root;Json::Reader reader; reader.parse(inStr, root);_result = root["result"].asUInt();_code = root["code"].asUInt();
#endifPrint();return true;}void Print(){std::cout << "_result: " << _result << std::endl;std::cout << "_code: " << _code << std::endl;}~Response(){}public:int _result;int _code; // 0 success; 1,2,3,4代表不同错误码};
}
完整代码:lesson36/NetCal_v2 · 遇健/Linux - 码云 - 开源中国 (gitee.com)
相关文章:

协议定制 + Json序列化反序列化
文章目录 协议定制 Json序列化反序列化1. 再谈 "协议"1.1 结构化数据1.2 序列化和反序列化 2. 网络版计算器2.1 服务端2.2 协议定制(1) 网络发送和读取的正确理解(2) 协议定制的问题 2.3 客户端2.4 代码 3. Json实现序列化反序列化3.1 简单介绍3.2 使用 协议定制 J…...

系统架构设计师(第二版)学习笔记----系统架构概述
【原文链接】系统架构设计师(第二版)学习笔记----系统架构概述 文章目录 一、系统架构的定义与发展历程1.1 架构的定义1.2 架构设计的作用1.3 架构设计产生的背景1.4 软件架构的发展历程1.5 模块化开发方法1.6 模块法方法分解模块遵循的原则1.7 软件工程…...

FPGA基本算术运算
FPGA基本算术运算 FPGA基本算术运算1 有符号数与无符号数2 浮点数及定点数I、定点数的加减法II、定点数的乘除法 3 仿真验证i、加减法验证ii、乘除法验证 FPGA基本算术运算 FPGA相对于MCU有并行计算、算法效率较高等优势,但同样由于没有成型的FPU等MCU内含的浮点数运…...

Linux Input子系统
一、基本概念 按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。本质属于字符设备。 1. input子系统结构如下: input 子系统分为 input 驱动层、input 核心层、input 事件处理层&…...

commet与websocket
commet与websocket Comet 前言 Comet是一种用于web的技术,能使服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求,目前有两种实现方式,长轮询和iframe流。 实现方式 长轮询 长轮询是在打开一条连接以后保持&…...

python3 简易 http server:实现本地与远程服务器传大文件
在个人目录下创建新文件httpserver.py : vim httpserver.py文件内容为python3代码: # !/usr/bin/env python3 import datetime import email import html import http.server import io import mimetypes import os import posixpath import re import…...

Microsoft Edge 主页启动diy以及常用的扩展、收藏夹的网站
一、Microsoft Edge 主页启动diy 二、常用的扩展 1、去广告:uBlock Origin 2、翻译: 页面翻译:右键就有了,已经内置了划词翻译 3、超级复制 三、收藏夹的网站...

文末送书!谈谈原型模式在JAVA实战开发中的应用(附源码+面试题)
作者主页:Designer 小郑 作者简介:3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN博客专家,蓝桥云课认证讲师。 本文讲解了 Java 设计模式中的原型模式,并给…...

视频汇聚/视频云存储/视频监控管理平台EasyCVR启动时打印starting server:listen tcp,该如何解决?
视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同,可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成,…...

【Linux从入门到精通】通信 | 管道通信(匿名管道 命名管道)
本派你文章主要是对进程通信进行详解。主要内容是介绍 为什么通信、怎么进行通信。其中本篇文章主要讲解的是管道通信。希望本篇文章会对你有所帮助。 文章目录 一、进程通信简单介绍 1、1 什么是进程通信 1、2 为什么要进行通信 1、3 进程通信的方式 二、匿名管道 2、1 什么是…...

实践和项目:解决实际问题时,选择合适的数据结构和算法
文章目录 选择合适的数据结构数组链表栈队列树图哈希表 选择合适的算法实践和项目 🎉欢迎来到数据结构学习专栏~实践和项目:解决实际问题时,选择合适的数据结构和算法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT…...

上线检查工具(待完善)
根据V11《CEBPM系统上线CheckList》整理而得,适用于V11,DHERP,Oracle和MSSQL数据库,检查内容还不完善。 上图: 1)数据库连接 2)双击[连接别名],可选择历史连接 3)主界面…...

PE文件格式详解
摘要 本文描述了Windows系统的PE文件格式。 PE文件格式简介 PE(Portable Executable)文件格式是一种Windows操作系统下的可执行文件格式。PE文件格式是由Microsoft基于COFF(Common Object File Format)格式所定义的,…...

【Alibaba中间件技术系列】「RocketMQ技术专题」RocketMQ消息发送的全部流程和落盘原理分析
RocketMQ目前在国内应该是比较流行的MQ 了,目前本人也在公司的项目中进行使用和研究,借着这个机会,分析一下RocketMQ 发送一条消息到存储一条消息的过程,这样会对以后大家分析和研究RocketMQ相关的问题有一定的帮助。 分析的总体…...
关于vue首屏加载loading问题
注意:网上搜索出来的都是教你在index.html里面<div id"app"><div class"loading"></div>或者在app.vue Mounte生命周期函数控制app和loading的显示和隐藏,这里会有一个问题,就是js渲染页面需要时间,一…...

数据库性能测试实践:慢查询统计分析
01、慢查询 查看是否开启慢查询 mysql> show variables like %slow%’; 如图所示: 系统变量log_slow_admin_statements 表示是否将慢管理语句例如ANALYZE TABLE和ALTER TABLE等记入慢查询日志启用log_slow_extra系统变量 (从MySQL 8.0.14开始提供&a…...
windows wsl ssh 配置流程 Permission denied (publickey)
wsl ssh连接失败配置流程 1、wsl2 ifconfig的网络ip是虚拟的ip,所以采用wsl1 2、wsl1的安装教程。 3、openssh-server重装 sudo apt-get update sudo apt-get remove openssh-server sudo apt-get install openssh-server4、修改ssh配置文件 sudo vim /etc/ss…...

OpenCV(五):图像颜色空间转换
目录 1.图像颜色空间介绍 RGB 颜色空间 2.HSV 颜色空间 3.RGBA 颜色空间 2.图像数据类型间的互相转换convertTo() 3.不同颜色空间互相转换cvtColor() 4.Android JNI demo 1.图像颜色空间介绍 RGB 颜色空间 RGB 颜色空间是最常见的颜色表示方式之一,其中 R、…...

一图胜千言!数据可视化多维讲解(Python)
数据聚合、汇总和可视化是支撑数据分析领域的三大支柱。长久以来,数据可视化都是一个强有力的工具,被业界广泛使用,却受限于 2 维。在本文中,作者将探索一些有效的多维数据可视化策略(范围从 1 维到 6 维)。…...
Hbase相关总结
Hbase 1、Hbase的数据写入流程 由客户端发起写入数据的请求, 首先会先连接zookeeper 从zookeeper中获取到当前HMaster的信息,并与HMaster建立连接从HMaster中获取RegionServer列表信息 连接meta表对应的RegionServer地址, 从meta表获取当前要写入的表对应region被那个RegionS…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...