C++服务器 支持http、tcp protobuf、websocket,linux开源框架 零依赖轻松编译部署 Reactor
开源地址: https://github.com/crust-hub/tubekit/tree/main
Github:https://github.com/gaowanlu
诚招有兴趣的小伙伴加入开发维护
Tubekit
The C++ TCP server framework based on the Reactor model continues to implement POSIX thread pool, Epoll, non blocking IO, object pool, log, socket network programming, support the dynamic library to implement custom protocol extensions, and use http parser to process http requests. Currently only supports Linux systems
Platform: Linux
Protocol: HTTP TCP Stream(Protobuf) WebSocket
Get Start
prepare
$ sudo apt update
$ sudo apt install protobuf-compiler libprotobuf-dev
$ apt install g++ cmake make
$ git clone https://github.com/crust-hub/tubekit.git
Build
$ cd tubekit
$ cd protocol
$ make
$ cd ..
$ cmake .
$ make -j3
Config
$ vim bin/config/main.ini
Run
$ chmod +x ./run.sh
$ ./run.sh
Stop
$ chmod +x ./kill.sh
$ ./kill.sh
App
support tcp keep-alive stream (protobuf) and http app (http-parser)、websocket
Directory Structure
Directory Structure Link
Third Party
@http-parser
@lua
HTTP样例
#include "app/http_app.h"
#include <string>
#include <vector>
#include <filesystem>
#include <tubekit-log/logger.h>#include "utility/mime_type.h"
#include "utility/url.h"using std::string;
using std::vector;
using tubekit::app::http_app;
using tubekit::connection::http_connection;
namespace fs = std::filesystem;
namespace utility = tubekit::utility;class html_loader
{
public:static string load(string body){static string frame1 = "<!DOCTYPE html>\<html>\<head>\<title></title>\</head>\<body>";static string frame2 = "</body>\</html>";return frame1 + body + frame2;}static string a_tag(string url, string text){string frame = "<a href=\"" + url + "\">" + text + "</a></br>";return frame;}
};void http_app::process_connection(tubekit::connection::http_connection &m_http_connection)
{m_http_connection.m_buffer.set_limit_max(202300);// load callbackm_http_connection.destory_callback = [](http_connection &m_connection) -> void{if (m_connection.ptr){FILE *file = (FILE *)m_connection.ptr;::fclose(file);m_connection.ptr = nullptr;}};m_http_connection.process_callback = [](http_connection &connection) -> void{string url = utility::url::decode(connection.url);auto find_res = url.find("..");if (std::string::npos != find_res){connection.set_response_end(true);return;}const string prefix = "/";fs::path t_path;if (url.empty() || url[0] != '/'){t_path = prefix + url;}else{t_path = url;}if (fs::exists(t_path) && fs::status(t_path).type() == fs::file_type::regular){std::string mime_type;try{mime_type = utility::mime_type::get_type(t_path.string());}catch (...){mime_type = "application/octet-stream";}std::string response = "HTTP/1.1 200 OK\r\nServer: tubekit\r\n";response += "Content-Type: ";response += mime_type + "\r\n\r\n";try{connection.m_buffer.write(response.c_str(), response.size());}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.ptr = nullptr;connection.ptr = ::fopen(t_path.c_str(), "r");if (connection.ptr == nullptr){connection.set_response_end(true);return;}// Write when the contents of the buffer have been sent write_end_callback will be executed,// and the response must be set response_end to true, then write after write_end_callback will be continuously recalledconnection.write_end_callback = [](http_connection &m_connection) -> void{char buf[202300] = {0};int len = 0;len = ::fread(buf, sizeof(char), 202300, (FILE *)m_connection.ptr);if (len > 0){try{m_connection.m_buffer.write(buf, len);}catch (const std::runtime_error &e){LOG_ERROR(e.what());}}else{m_connection.set_response_end(true);}};return;}if (fs::exists(t_path) && fs::status(t_path).type() == fs::file_type::directory){connection.ptr = nullptr;const char *response = "HTTP/1.1 200 OK\r\nServer: tubekit\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n";try{connection.m_buffer.write(response, strlen(response));}catch (const std::runtime_error &e){LOG_ERROR(e.what());}// generate dir listvector<string> a_tags;for (const auto &dir_entry : fs::directory_iterator{t_path}){std::string sub_path = dir_entry.path().string(); //.substr(prefix.size());a_tags.push_back(html_loader::a_tag(utility::url::encode(sub_path), sub_path));}string body;for (const auto &a_tag : a_tags){body += a_tag;}string html = html_loader::load(body);try{connection.m_buffer.write(html.c_str(), html.size());}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.set_response_end(true);return;}const char *response = "HTTP/1.1 404 Not Found\r\nServer: tubekit\r\nContent-Type: text/text; charset=UTF-8\r\n\r\n";try{connection.m_buffer.write(response, strlen(response));}catch (const std::runtime_error &e){LOG_ERROR(e.what());}connection.set_response_end(true);};
}
protobuf样例
#include "app/stream_app.h"
#include "proto_res/proto_cmd.pb.h"
#include "proto_res/proto_example.pb.h"
#include "proto_res/proto_message_head.pb.h"
#include <tubekit-log/logger.h>
#include <string>
#include <set>
#include "thread/mutex.h"
#include "utility/singleton.h"
#include "connection/connection_mgr.h"
#include "socket/socket.h"
#include "socket/socket_handler.h"using tubekit::app::stream_app;
using tubekit::connection::connection_mgr;
using tubekit::connection::stream_connection;
using tubekit::socket::socket;
using tubekit::socket::socket_handler;
using tubekit::utility::singleton;namespace tubekit::app
{std::set<void *> global_player;tubekit::thread::mutex global_player_mutex;
}int process_protocol(tubekit::connection::stream_connection &m_stream_connection, ProtoPackage &package)
{// EXAMPLE_REQif (package.cmd() == ProtoCmd::EXAMPLE_REQ){ProtoExampleReq exampleReq;if (exampleReq.ParseFromString(package.body())){LOG_ERROR("%s", exampleReq.testcontext().c_str());// std::cout << exampleReq.testcontext() << std::endl;}else{return -1;}return 0;}return -1;
}void stream_app::process_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;uint64_t all_data_len = m_stream_connection.m_recv_buffer.can_readable_size();char *all_data_buffer = new char[all_data_len];m_stream_connection.m_recv_buffer.copy_all(all_data_buffer, all_data_len);uint64_t offset = 0;do{char *tmp_buffer = all_data_buffer + offset;uint64_t data_len = all_data_len - offset;if (data_len == 0){break;}ProtoPackage protoPackage;if (!protoPackage.ParseFromArray(tmp_buffer, data_len)){// std::cout << "protoPackage.ParseFromArray failed" << std::endl;break;}if (0 != process_protocol(m_stream_connection, protoPackage)){// std::cout << "process_protocol failed" << std::endl;m_stream_connection.mark_close();m_stream_connection.m_recv_buffer.clear();break;}// std::cout << "datalen " << data_len << " package size " << protoPackage.ByteSizeLong() << std::endl;offset += protoPackage.ByteSizeLong();} while (true);if (!m_stream_connection.m_recv_buffer.read_ptr_move_n(offset)){m_stream_connection.mark_close();}delete[] all_data_buffer;
}void stream_app::on_close_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;global_player_mutex.lock();global_player.erase(m_stream_connection.get_socket_ptr());LOG_ERROR("player online %d", global_player.size());global_player_mutex.unlock();
}void stream_app::on_new_connection(tubekit::connection::stream_connection &m_stream_connection)
{using tubekit::app::global_player;using tubekit::app::global_player_mutex;global_player_mutex.lock();global_player.insert(m_stream_connection.get_socket_ptr());LOG_ERROR("player online %d", global_player.size());global_player_mutex.unlock();
}bool stream_app::new_client_connection(const std::string &ip, int port)
{socket::socket *socket_object = singleton<socket_handler>::instance()->alloc_socket();if (!socket_object){LOG_ERROR("alloc_socket return nullptr");return false;}bool b_ret = socket_object->connect(ip, port);if (!b_ret){LOG_ERROR("connection remote %s:%d failed", ip.c_str(), port);singleton<socket_handler>::instance()->remove(socket_object);return false;}int i_ret = singleton<socket_handler>::instance()->attach(socket_object);if (0 != i_ret){LOG_ERROR("attach to socket_handler error ret %d", i_ret);singleton<socket_handler>::instance()->remove(socket_object);return false;}// maybe to do some management for client socket...return true;
}
websocket
#include "app/websocket_app.h"
#include <vector>
#include <tubekit-log/logger.h>
#include "utility/singleton.h"
#include "connection/connection_mgr.h"
#include <arpa/inet.h>using namespace tubekit::app;
using namespace tubekit::utility;
using namespace tubekit::connection;struct websocket_frame
{uint8_t fin;uint8_t opcode;uint8_t mask;uint64_t payload_length;std::vector<uint8_t> masking_key;std::string payload_data;
};enum class websocket_frame_type
{CONNECTION_CLOSE_FRAME = 0,TEXT_FRAME = 1,BINARY_FRAME = 2,PONG = 3,PING = 4,CONTINUATION_FRAME = 5,ERROR = 6
};void websocket_app::process_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("process_connection");uint64_t all_data_len = m_websocket_connection.m_recv_buffer.can_readable_size();if (all_data_len <= 0){LOG_ERROR("all_data_len <= 0");return;}char *data = new (std::nothrow) char[all_data_len];if (!data){return;}all_data_len = m_websocket_connection.m_recv_buffer.copy_all(data, all_data_len);size_t index = 0;while (true){if (index >= all_data_len){break;}size_t start_index = index;websocket_frame frame;websocket_frame_type type = websocket_frame_type::ERROR;switch ((uint8_t)data[index]){case 0x81:{type = websocket_frame_type::TEXT_FRAME;break;}case 0x82:{type = websocket_frame_type::BINARY_FRAME;break;}case 0x88:{type = websocket_frame_type::CONNECTION_CLOSE_FRAME;break;}case 0x89:{type = websocket_frame_type::PING;break;}default:{if (data[index] >= 0x00 && data[index] <= 0x7F){type = websocket_frame_type::CONTINUATION_FRAME;}break;}}if (type != websocket_frame_type::TEXT_FRAME && type != websocket_frame_type::BINARY_FRAME){m_websocket_connection.mark_close();break;}frame.fin = (data[index] & 0x80) != 0;frame.opcode = data[index] & 0x0F;index++;if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}frame.mask = (data[index] & 0x80) != 0;frame.payload_length = data[index] & 0x7F;index++;if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}if (frame.payload_length == 126){frame.payload_length = 0;if (index + 2 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 2, all_data_len);break;}uint16_t tmp = 0;u_char *ph;ph = (u_char *)&tmp;*ph++ = data[index];*ph++ = data[index + 1];tmp = ntohs(tmp);frame.payload_length = tmp;index += 2;}else if (frame.payload_length == 127){frame.payload_length = 0;if (index + 8 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 8, all_data_len);break;}uint32_t tmp = 0;u_char *ph = (u_char *)&tmp;*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];frame.payload_length = ntohl(tmp);frame.payload_length = frame.payload_length << 32;ph = (u_char *)&tmp;*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];*ph++ = data[index++];tmp = ntohl(tmp);frame.payload_length = frame.payload_length | tmp;}if (frame.payload_length == 0){break;}if (frame.mask){if (index + 4 >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index + 3, all_data_len);break;}frame.masking_key = {(uint8_t)data[index], (uint8_t)data[index + 1], (uint8_t)data[index + 2], (uint8_t)data[index + 3]};index += 4;}// payload data [data+index,data+index+frame.payload_length]if (index >= all_data_len){LOG_ERROR("index[%llu] >= all_data_len[%llu]", index, all_data_len);break;}if (index - 1 + frame.payload_length >= all_data_len){LOG_ERROR("index - 1 + frame.payload_length=[%llu] >= all_data_len[%llu]", index - 1 + frame.payload_length, all_data_len);break;}std::string payload_data(data + index, frame.payload_length);if (frame.mask){for (size_t i = 0; i < payload_data.size(); ++i){payload_data[i] ^= frame.masking_key[i % 4];}}frame.payload_data = std::move(payload_data);// broadcastsingleton<connection_mgr>::instance()->for_each([&frame](connection::connection &conn) -> void{websocket_connection *ptr_conn = static_cast<websocket_connection *>(&conn);websocket_app::send_packet(*ptr_conn, frame.payload_data.c_str(), frame.payload_length, false);});// websocket_app::send_packet(m_websocket_connection, frame.payload_data.c_str(), frame.payload_length, false);// frame.payload_data.push_back(0);// LOG_ERROR("%s", frame.payload_data.c_str());m_websocket_connection.m_recv_buffer.read_ptr_move_n(index - start_index + frame.payload_length);index += frame.payload_length;}delete[] data;
}void websocket_app::on_close_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("on_close_connection");
}void websocket_app::on_new_connection(tubekit::connection::websocket_connection &m_websocket_connection)
{LOG_ERROR("on_new_connection");
}bool websocket_app::send_packet(tubekit::connection::websocket_connection &m_websocket_connection, const char *data, size_t data_len, bool use_safe)
{if (!data){return false;}uint8_t opcode = 0x81;size_t message_length = data_len;std::vector<uint8_t> frame;frame.push_back(opcode);if (message_length <= 125){frame.push_back(static_cast<uint8_t>(message_length));}else if (message_length <= 0xFFFF){frame.push_back(126);frame.push_back((message_length >> 8) & 0xFF);frame.push_back(message_length & 0xFF);}else{frame.push_back(127);for (int i = 7; i >= 0; --i){frame.push_back((message_length >> (8 * i)) & 0xFF);}}frame.insert(frame.end(), data, data + data_len);if (!use_safe){return m_websocket_connection.send((const char *)frame.data(), frame.size());}return singleton<connection_mgr>::instance()->safe_send(m_websocket_connection.get_socket_ptr(), (const char *)frame.data(), frame.size());
}
相关文章:
C++服务器 支持http、tcp protobuf、websocket,linux开源框架 零依赖轻松编译部署 Reactor
开源地址: https://github.com/crust-hub/tubekit/tree/main Github:https://github.com/gaowanlu 诚招有兴趣的小伙伴加入开发维护 Tubekit The C TCP server framework based on the Reactor model continues to implement POSIX thread pool, Epoll, non blocking IO, obj…...
1688API接口系列,1688开放平台接口使用方案(商品详情数据+搜索商品列表+商家订单类)
1688商品详情接口是指1688平台提供的API接口,用于获取商品详情信息。通过该接口,您可以获取到商品的详细信息,包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口,您需要先申请1688的API权限,并获取ac…...
CentOS服务器网页版Rstudio-server及R包批量安装最佳实践
CentOS服务器安装网页版Rstudio-server及R包批量安装 以下为CentOS 7/8的Rstudio-server安装、配置和R包安装操作 1. 软件包安装 Centos 7安装 # 下载安装包,大小115.14 MB wget -c https://download2.rstudio.org/server/centos7/x86_64/rstudio-server-rhel-…...
centos7内核升级(k8s基础篇)
1.查看系统内核版本信息 uname -r 2.升级内核 2.1更新yum源仓库 yum -y update更新完成后,启用 ELRepo 仓库并安装ELRepo仓库的yum源 ELRepo 仓库是基于社区的用于企业级 Linux 仓库,提供对 RedHat Enterprise (RHEL) 和 其他基于 RHEL的 Linux 发行…...
数据结构与算法设计分析——NP完全理论
目录 一、P类问题与NP类问题的定义二、常见的NP类问题(一)旅行商问题(TSP)(二)哈密尔顿回路问题(三)判断回路问题(四)图的着色问题(五)…...
AGNES层次聚类
已知数据集D中有9个数据点,分别是(1,2),(2,3),(2,1), (3,1),(2,4),(3,5),(4,3),(1,5),(4,2)。要求: (1)采用层次聚类的聚集算法进行聚类,k2。 (2)距离计算采用欧几里得距离。 (3)簇之间的距离采用单链接方…...
HCIP —— 双点重发布 + 路由策略 实验
目录 实验拓扑: 实验要求: 实验配置: 1.配置IP地址 2.配置动态路由协议 —— RIP 、 OSPF R1 RIP R4 OSPF R2 配置RIP、OSPF 双向重发布 R3配置RIP、OSPF 双向重发布 3.查询路由表学习情况 4.使用路由策略控制选路 R2 R3 5.检…...
Python标准库:datetime模块【侯小啾python领航班系列(二十五)】
Python标准库:datetime模块【侯小啾python领航班系列(二十五)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…...
新版idea如何开启多台JVM虚拟机
1.看看自己的项目 2.可能开始的时候啥也没有,就点Run Configuration Type 3.再点击Edit Configurations... 4.点击号添加SpringBoot 5.主类选择一下,一般就一个,点他选了就行。 6.然后点击Modify Options 选择添加add VM Options 7.点击appl…...
软件工程单选多选补充
2. 4. 5. 6. 7. 8. 9. 10. 12。 13....
6-66.时间
本题要求输入小时、分钟和秒数,并将其输出。针对时间表示中出现的异常进行处理。例如小时数不应超过23,分钟不应超过59,秒数不应超过59。此外,以上三个变量均应大于等于0。 输入样例: 在这里给出三组输入。例如&…...
面试多线程八股文十问十答第一期
面试多线程八股文十问十答第一期 作者:程序员小白条,个人博客 相信看了本文后,对你的面试是有一定帮助的! ⭐点赞⭐收藏⭐不迷路!⭐ 1.ThreadLocal如何实现线程安全 Java的ThreadLocal是一个线程本地变量࿰…...
Mybatis 操作续集(结合上文)
当我们增加一个数据之后,如果我们想要获取它的 Id 进行别的操作,我们该如何获取 Id 呢? 用那个Options package com.example.mybatisdemo.mapper;import com.example.mybatisdemo.model.UserInfo; import org.apache.ibatis.annotations.*;import java.util.List;Mapper pub…...
JVM基础篇:垃圾回收
目录 1.前言 1.1C/C的内存管理 1.2Java的内存管理 2.方法区的回收 3.堆回收 3.1引用计数法和可达性分析法 3.2五种对象引用 强引用 软引用 弱引用 虚引用 终结器引用 3.3垃圾回收算法评价标准 ①吞吐量 ②最大暂停时间 ③堆使用效率 3.4垃圾回收算法 ①标记清…...
蓝桥杯ACwing习题
题目 :https://www.acwing.com/problem/content/4409/ 解析 :根据题目我们可以知道 问的是方案数 那么首先就想到了 dp 仔细想一下 发现类似于蒙德里安的梦想那道状态压缩的题 , 所以我们先考虑怎么定义 f[i][j] f[i][j] 表示的是 已经放了…...
vue发送请求携带token,拼接url地址下载文件
封装请求 ,该请求为普通的get请求 该请求返回值为: 请求成功之后拼接URL地址下载文件 代码块 downTemplateRequest(activeKeys.value).then((res) > {let url http://47.169.168.99:18888/media/${res.data.name};var elink document.createElemen…...
【PTA-C语言】编程练习3 - 循环结构Ⅱ
如果代码存在问题,麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 编程练习3 - 循环结构(9~15) 7-9 特殊a串数列求和(分数 15)7-10 穷举法搬运砖块问题(分数 15)7-11 数字金字塔(分数 15&…...
Google Chrome 下载 (离线版)
1 访问网址 Google Chrome 网络浏览器 2 点击 下载Chrome 3 直接运行 ChromeStandaloneSetup64.exe 其他: ####################### 谷歌浏览器 (Google Chrome) 最新版离线安装包下载 https://www.iplaysoft.com/tools/chrome/#google_vignette Google Chrome …...
2023年GopherChina大会-核心PPT资料下载
一、峰会简介 自 Go 语言诞生以来,中国便是其应用最早和最广的国家之一,根据 Jetbrains 在 2021 年初做的调查报告,总体来说目前大概有 110 万专业的开发者 选择 Go 作为其主要开发语言。就其全球分布而言, 居住在亚洲的开发者最多ÿ…...
从源代码出发,Jenkins 任务排队时间过长问题的解决过程
最近开发了一个部署相关的工具,使用 Jenkins 来构建应用。Jenkins 的任务从模板中创建而来。每次部署时,通过 Jenkins API 来触发构建任务。在线上运行时发现,通过 API 触发的 Jenkins 任务总是会时不时在队列中等待较长的时间。某些情况下的…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
