【Linux网络】应用层自定义协议与序列化及Socket模拟封装
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 🏳️🌈一、应用层
- 1.1 再谈 "协议"
- 1.2 网络版计算器
- 1.3 序列化和反序列化
- 🏳️🌈二、什么是全双工
- 🏳️🌈三、Socket封装
- 3.1 整体结构
- 3.2 Socket 基类
- 3.3 TcpSocket 子类
- 3.3.1 基本结构
- 3.3.2 构造、析构函数
- 3.3.3 创建套接字
- 3.3.4 绑定套接字
- 3.3.5 监听套接字
- 3.3.6 获取连接
- 3.3.7 建立连接
- 3.3.8 其他方法
- 3.3.10 整体代码
- 👥总结
11111111
11111111
11111111
11111111
**** 11111111
🏳️🌈一、应用层
我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层.
1.1 再谈 “协议”
协议是一种 “约定”. socket api 的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些 “结构化的数据” 怎么办呢?
其实,协议就是双方约定好的结构化的数据!
1.2 网络版计算器
例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端.
约定方案一(传结构体对象):
- 客户端发送一个形如"1+1"的字符串;
- 这个字符串中有两个操作数, 都是整形;
- 两个数字之间会有一个字符是运算符, 运算符只能是 + ;
- 数字和运算符之间没有空格;
- …
不推荐直接传结构体对象,从技术和业务角度解释?
1、技术角度
-
1、跨平台与兼容性:
- 结构体的大小和内存布局可能因编译器、操作系统或硬件平台的不同而有所差异。这可能导致在一个平台上发送的结构体在另一个平台上无法正确解析。
-
2、内存对齐与填充:
- 为了优化内存访问速度,编译器可能会对结构体成员进行对齐和填充。这会导致结构体的实际大小大于其成员大小的总和。
- 直接传输结构体可能会因为内存对齐和填充的问题而导致数据解析错误。
-
3、指针与动态内存:
- 结构体中可能包含指针,这些指针指向动态分配的内存。直接传输结构体无法传递指针所指向的数据,而只能传递指针值,这可能导致数据丢失或内存泄漏。
2、业务角度
- 1、数据安全性:
- 直接传输结构体可能会暴露数据的内部结构和实现细节,从而增加数据被恶意攻击的风险。
- 2、数据版本控制:
- 随着业务的发展和变化,数据结构和格式可能需要进行调整和升级。
约定方案二(传字符串):
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
- 这个过程叫做 “
序列化
” 和 “反序列化
”
1.3 序列化和反序列化
无论我们采用方案一, 还是方案二, 还是其他的方案, 只要保证, 一端发送时构造的数据,在另一端能够正确的进行解析, 就是 ok 的. 这种约定, 就是 应用层协议
但是,为了让我们深刻理解协议,我们打算自定义实现一下协议的过程。
- 我们采用方案 2,我们也要体现协议定制的细节
- 我们要引入序列化和反序列化,只不过我们课堂直接采用现成的方案 – jsoncpp库
- 我们要对
socket
进行字节流的读取处理
🏳️🌈二、什么是全双工
所以:
- 在任何一台主机上,TCP 连接既有发送缓冲区,又有接受缓冲区,所以,在内核中,可以在发消息的同时,也可以收消息,即全双工
- 这就是为什么一个 tcp sockfd 读写都是它的原因
- 实际数据什么时候发,发多少,出错了怎么办,由 TCP 控制,所以 TCP 叫做传输控制协议
1、read,write,send,recv本质是拷贝函数!
2、发送数据的本质:是从发送方的发送缓冲区把数据通过协议栈和网络拷贝给接收方大的接收缓冲区!
3、tcp支持全双工通信的原因(有发送和接收缓冲区)!
4、有两个缓冲区这种模式就是生产者消费者模型!
5、为什么IO函数要阻塞?本质是在维护同步关系!
TCP协议是面向字节流的,客户端发的,不一定是全部是服务端收的,怎么保证读到的是一个完整的请求呢?
- 分割完整的报文!
🏳️🌈三、Socket封装
Socket类以模板方法类的设计模式进行封装,将算法的不变部分封装在抽象基类中 ,而将可变部分延迟到子类中实现!
3.1 整体结构
-
抽象类(Abstract Class):
- 定义了多个抽象操作,这些操作在抽象类中不具体实现,由子类实现。
- 定义了两个模板方法,这个方法通常调用了上面提到的抽象操作。模板方法的算法骨架是固定的,但其中一些步骤的具体实现会延迟到子类中。
-
具体子类(Concrete Class):
- 实现抽象类中的抽象操作,提供具体的算法步骤实现。
- 可以重写父类中的模板方法,但通常情况下不需要这么做,因为模板方法已经在抽象类中定义好了算法的骨架。
3.2 Socket 基类
将套接字创建、绑定、监听等 通用流程 抽象为模板方法,如 BuildListenSocket,而将具体步骤(如 CreaterSocketOrDie)延迟到子类实现。
角色划分:
- 抽象类(Abstract Class):Socket 定义了纯虚函数(步骤方法)和模板方法(流程框架)。
- 具体子类(Concrete Class):由用户继承 Socket 并实现纯虚函数(例如 TcpSocket、UdpSocket)。
using SockPtr = std::shared_ptr<Socket>;class Socket {public:virtual void CreateSocketOrDie() = 0; // 创建套接字virtual void BindOrDie(uint16_t port) = 0; // 绑定套接字virtual void ListenOrDie(int backlog = gbacklog) = 0; // 监听套接字virtual SockPtr Accepter(InetAddr* cli) = 0; // 获取链接virtual bool Connector(const std::string& peerip,uint16_t peerport) = 0; // 简历连接virtual int Sockfd() = 0;virtual void Close() = 0;virtual ssize_t Recv(std::string* out) = 0; // 接收数据virtual ssize_t Send(const std::string& in) = 0; // 发送数据public:// 创建监听套接字void BuildListenSocket(uint16_t port) {CreateSocketOrDie(); // 创建BindOrDie(port); // 绑定ListenOrDie(); // 监听}// 创建客户端套接字void BuildConnectorSocket(const std::string& peerip, uint16_t peerport) {CreateSocketOrDie(); // 创建Connector(peerip, peerport); // 连接}
};
3.3 TcpSocket 子类
TcpSocket类
继承Socket类
,并具体实现父类的抽象操作!
3.3.1 基本结构
TcpSocket 子类就是具体实现父类的抽象操作,所以所有 TCP 可能会用到的父类方法都要具体实现
class TcpSocket : public Socket {
public:TcpSocket() {}TcpSocket(int sockfd) {}// 创建套接字void CreateSocketOrDie() {}// 绑定套接字void BindOrDie(uint16_t port) {}// 监听套接字void ListenOrDie(int backlog = gbacklog) {}// 获取链接SockPtr Accepter(InetAddr* cli) {}// 建立连接bool Connector(const std::string& peerip, uint16_t peerport) {}// 获取套接字描述符int Sockfd() {}// 关闭套接字void Close() {}~TcpSocket() {}private:int _sockfd;
};
3.3.2 构造、析构函数
- 构造函数 可以实现两个,一个无参构造,一个有参构造(传参sockfd),用于初始化成员变量
- 析构函数 可以不做处理,后面关闭套接字自己调用关闭函数即可!
TcpSocket() {}
TcpSocket(int sockfd) : _sockfd(sockfd) {}
~TcpSocket() {}
3.3.3 创建套接字
使用库函数 socket 按照格式创建套接字
// 创建套接字
void CreateSocketOrDie() override {_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0) {LOG(LogLevel::ERROR) << "create socket error";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd create success : " << _sockfd;
}
3.3.4 绑定套接字
这部分用于服务端,所以需要先构建服务端的网络字节序地址,然后绑定套接字和网络字节序地址
// 绑定套接字void BindOrDie(uint16_t port) override{// sockaddr_in 的头文件是 #include <netinet/in.h>struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = htonl(INADDR_ANY);int n = ::bind(_sockfd, CONV(&local), sizeof(local));if(n < 0){LOG(LogLevel::ERROR) << "bind socket error";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success";}
3.3.5 监听套接字
在绑定好网络字节序地址后,我们需要形成老板模式,设置最大连接数量,然后不断监听
// 监听套接字
void ListenOrDie(int backlog = gbacklog) override {int n = ::listen(_sockfd, backlog);if (n < 0) {LOG(LogLevel::ERROR) << "listen socket error";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen success";
}
3.3.6 获取连接
在为监听套接字设置好最大连接长度后,我们不断使用 accept 监听这个套接字,将获取到的客户端ip和端口号等信息 与 我们的服务端的连接 整合起来,形成一个整体套接字,实现面向对象连接
// 获取链接
SockPtr Accepter(InetAddr* cli) override {struct sockaddr_in client;socklen_t clientlen = sizeof(client);// accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)// 返回一个新的套接字,该套接字与调用进程间接地建立了连接。int sockfd = ::accept(_sockfd, CONV(&client), &clientlen);if (sockfd < 0) {LOG(LogLevel::ERROR) << "accept socket error";return nullptr;}*cli = InetAddr(client);LOG(LogLevel::DEBUG) << "get a new connection from "<< cli->AddrStr().c_str() << ", sockfd : " << sockfd;return std::make_shared<TcpSocket>(sockfd);
}
3.3.7 建立连接
当客户端想要连接上服务端的时候,我们需要先为服务端创建一个网络字节序地址,再与客户端的套接字连接起来
// 建立连接
bool Connector(const std::string& serverip, uint16_t serverport) override {struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET; // IPv4协议server.sin_port = htons(serverport); // 端口号// 这句话表示将字符串形式的IP地址转换为网络字节序的IP地址// inet_pton函数的作用是将点分十进制的IP地址转换为网络字节序的IP地址// 这里的AF_INET表示IPv4协议// 这里的serverip.c_str()表示IP地址的字符串形式// &server.sin_addr表示将IP地址存储到sin_addr成员变量中::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // IP地址int n = ::connect(_sockfd, CONV(&server), sizeof(server));if (n < 0) {LOG(LogLevel::ERROR) << "connect socket error";return false;}LOG(LogLevel::DEBUG) << "connect success";return true;
}
3.3.8 其他方法
还有一些其他的方法,难度不大,就不一一介绍了
// 获取套接字描述符
int Sockfd() override { return _sockfd; }// 关闭套接字
void Close() override {if (_sockfd >= 0)::close(_sockfd);
}// 接收数据
ssize_t Recv(std::string* out) override {char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if (n > 0) {inbuffer[n] = 0;*out = inbuffer;}return n;
}// 发送数据
ssize_t Send(const std::string& in) override {return ::send(_sockfd, in.c_str(), in.size(), 0);
}
3.3.10 整体代码
#pragma once #include <iostream>
#include <cstring>
#include <Socket.h>
#include <memory>#include <netinet/in.h>#include "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"using namespace LogModule;const int gbacklog = 8;// common.hpp
// #define Die(code) \
// do \
// { \
// exit(code); \
// } while (0)// #define CONV(v) (struct sockaddr *)(v)// enum
// {
// USAGE_ERR = 1,
// SOCKET_ERR,
// BIND_ERR,
// LISTEN_ERR,
// CONNECTION_ERR
// };namespace SocketModule{using SockPtr = std::shared_ptr<Socket>;class Socket{public:virtual void CreateSocketOrDie() = 0; // 创建套接字virtual void BindOrDie(uint16_t port) = 0; // 绑定套接字virtual void ListenOrDie(int backlog = gbacklog) = 0; // 监听套接字virtual SockPtr Accepter(InetAddr* cli) = 0; // 获取链接virtual bool Connector(const std::string& serverip, uint16_t serverport) = 0; // 简历连接virtual int Sockfd() = 0;virtual void Close() = 0;virtual ssize_t Recv(std::string* out) = 0; // 接收数据virtual ssize_t Send(const std::string& in) = 0; // 发送数据 public:// 创建监听套接字void BuildListenSocket(uint16_t port){CreateSocketOrDie(); // 创建BindOrDie(port); // 绑定ListenOrDie(); // 监听}// 创建客户端套接字void BuildConnectorSocket(const std::string& serverip, uint16_t serverport){CreateSocketOrDie(); // 创建Connector(serverip, serverport); // 连接}};class TcpSocket : public Socket{public:TcpSocket(){}TcpSocket(int sockfd) : _sockfd(sockfd){ }// 创建套接字void CreateSocketOrDie() override{_sockfd = socket(AF_INET, SOCK_STREAM, 0);if(_sockfd < 0){LOG(LogLevel::ERROR) << "create socket error";exit(SOCKET_ERR);}LOG(LogLevel::DEBUG) << "sockfd create success : " << _sockfd;}// 绑定套接字void BindOrDie(uint16_t port) override{// sockaddr_in 的头文件是 #include <netinet/in.h>struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = htonl(INADDR_ANY);int n = ::bind(_sockfd, CONV(&local), sizeof(local));if(n < 0){LOG(LogLevel::ERROR) << "bind socket error";exit(BIND_ERR);}LOG(LogLevel::DEBUG) << "bind success";} // 监听套接字void ListenOrDie(int backlog = gbacklog) override{int n = ::listen(_sockfd, backlog);if(n < 0){LOG(LogLevel::ERROR) << "listen socket error";exit(LISTEN_ERR);}LOG(LogLevel::DEBUG) << "listen success";}// 获取链接SockPtr Accepter(InetAddr* cli) override{struct sockaddr_in client;socklen_t clientlen = sizeof(client);// accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)// 返回一个新的套接字,该套接字与调用进程间接地建立了连接。int sockfd = ::accept(_sockfd, CONV(&client), &clientlen);if(sockfd < 0){LOG(LogLevel::ERROR) << "accept socket error";return nullptr;}*cli = InetAddr(client);LOG(LogLevel::DEBUG) << "get a new connection from " << cli->AddrStr().c_str() << ", sockfd : " << sockfd;return std::make_shared<TcpSocket>(sockfd);}// 建立连接bool Connector(const std::string& serverip, uint16_t serverport) override{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET; // IPv4协议server.sin_port = htons(serverport); // 端口号// 这句话表示将字符串形式的IP地址转换为网络字节序的IP地址// inet_pton函数的作用是将点分十进制的IP地址转换为网络字节序的IP地址// 这里的AF_INET表示IPv4协议// 这里的serverip.c_str()表示IP地址的字符串形式// &server.sin_addr表示将IP地址存储到sin_addr成员变量中::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); // IP地址int n = ::connect(_sockfd, CONV(&server), sizeof(server));if(n < 0){LOG(LogLevel::ERROR) << "connect socket error" ;return false;}LOG(LogLevel::DEBUG) << "connect success";return true;}// 获取套接字描述符int Sockfd() override{ return _sockfd; }// 关闭套接字void Close() override{ if(_sockfd >= 0) ::close(_sockfd); }// 接收数据ssize_t Recv(std::string* out) override{char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if(n > 0){inbuffer[n] = 0;*out = inbuffer;}return n;} // 发送数据 ssize_t Send(const std::string& in) override{return ::send(_sockfd, in.c_str(), in.size(), 0);}~TcpSocket(){}private:int _sockfd;};
}
👥总结
本篇博文对 【Linux网络】应用层自定义协议与序列化及Socket模拟封装 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:
【Linux网络】应用层自定义协议与序列化及Socket模拟封装
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
Rust项目GPG签名配置指南
Rust项目GPG签名配置指南 一、环境准备 # 安装Gpg4win(Windows) winget install -e --id GnuPG.Gpg4win二、密钥生成与配置 # 生成RSA4096密钥 gpg --full-generate-key # 类型选RSA and RSA,长度4096,邮箱填z3266420686202216…...
6.第六章:数据分类的技术体系
文章目录 6.1 数据分类的技术架构6.1.1 数据分类的整体流程6.1.2 数据分类的技术组件6.1.2.1 数据采集与预处理6.1.2.2 特征工程与选择6.1.2.3 分类模型构建6.1.2.4 模型评估与优化6.1.2.5 分类结果应用与反馈 6.2 数据分类的核心技术与算法6.2.1 传统机器学习算法6.2.2 深度学…...
Nginx 反向代理,啥是“反向代理“啊,为啥叫“反向“代理?而不叫“正向”代理?它能干哈?
Nginx 反向代理的理解与配置 User 我打包了我的前端vue项目,上传到服务器,在宝塔面板安装了nginx服务,配置了文件 nginx.txt .运行了项目。 我想清楚,什么是nginx反向代理?是nginx作为一个中介?中间件来集…...
下篇:深入剖析 BLE GATT / GAP / SMP 与应用层(约5000字)
引言 在 BLE 协议栈的最上层,GAP 定义设备角色与连接管理,GATT 构建服务与特征,SMP 负责安全保障,应用层则承载具体业务逻辑与 Profile。掌握这一层,可实现安全可靠的设备发现、配对、服务交互和定制化业务。本文将详解 GAP、GATT、SMP 三大模块,并通过示例、PlantUML 时…...
Linux Awk 深度解析:10个生产级自动化与云原生场景
看图猜诗,你有任何想法都可以在评论区留言哦~ 摘要 Awk 作为 Linux 文本处理三剑客中的“数据工程师”,凭借字段分割、模式匹配和数学运算三位一体的能力,成为处理结构化文本(日志、CSV、配置文件)的终极工具。本文聚…...
无人设备遥控之调度自动化技术篇
无人设备遥控器的调度自动化技术是现代科技发展的重要成果,它通过集成先进的通信、控制、传感器及人工智能技术,实现了对无人设备的高效、精准调度与自动化管理。 一、核心技术 无线通信技术 调度自动化依赖于高速、稳定的无线通信网络(如5…...
STM32F407 HAL库使用 DMA_Normal 模式实现 UART 循环发送(无需中断)
在 STM32 开发中,很多人喜欢使用 DMA 来加速串口发送数据。然而,默认的 DMA 往往配合中断或使用循环模式(DMA_CIRCULAR)使用。但在某些特定需求下,我们希望: 使用 DMA_NORMAL 模式,确保 DMA 每次…...
汽车自动驾驶介绍
0 Preface/Foreword 1 介绍 1.1 FSD FSD: Full Self-Driving,完全自动驾驶 (Tesla) 1.2 自动驾驶级别 L0 - L2:辅助驾驶L3:有条件自动驾驶L4/5 :高度/完全自动驾驶...
Uniapp-小程序从入门到精通
沉淀UNIAPP项目精华模版 ******************************************************************************************************************************************* 1、数据库的导入SQL **************************************************************************…...
深度剖析操作系统核心(第一节):从X86/ARM/MIPS处理器架构到虚拟内存、分段分页、Linux内存管理,再揭秘进程线程限制与优化秘籍,助你成为OS高手!
文章目录 OS处理器X86ARMMIPSPowerPC 内存管理虚拟内存内存分段内存分页段页式内存管理Linux 内存管理 OS 处理器 常见处理器有X86、ARM、MIPS、PowerPC四种。 X86 X86架构是芯片巨头Intel设计制造的一种微处理器体系结构的统称。如果这样说你不理解,那么当我说…...
基于 EFISH-SBC-RK3588 的无人机通信云端数据处理模块方案
一、硬件架构设计 核心计算单元(EFISH-SBC-RK3588) 异构计算能力:搭载 8 核 ARM 架构(4Cortex-A762.4GHz 4Cortex-A551.8GHz),集成 6 TOPS NPU 与 Mali-G610 GPU,支持多任务并行处理…...
Unity 内置Standard Shader UNITY_BRDF_PBS函数分析 (二)
四、BRDF1_Unity_PBS // 主物理基BRDF实现 // 基于Disney工作并以Torrance-Sparrow微面模型为基础 // 公式: // BRDF kD / π kS * (D * V * F) / 4 // I BRDF * (N L) // // * NDF(法线分布函数)可根据 UNITY_BRDF_GGX 选择&#…...
GitHub万星项目维护者分享:开源协作的避坑指南
GitHub万星项目维护者分享:开源协作的避坑指南 ——开发者张三与237个文件改动PR的五年战争 序幕:深夜的炸弹 2019年夏天,张三维护的开源项目TerminalX刚突破8000星,一个标题猩红的PR突然弹出:“彻底重构࿰…...
Linux基础篇、第四章_01软件安装rpm_yum_源码安装_二进制安装
Linux基础篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! ————laowang 基础命令:rpm、yum、源码安装、二进制安装 一、rpm本地安装: (无需网络安装,无法解决软件依赖) rpm -ivh …...
焊接机排错
焊接机 一、前定位后焊接 两个机台,①极柱定位,相机定位所有极柱点和mark点;②焊接机,相机定位mark点原理:极柱定位在成功定位到所有极柱点和mark点后,可以建立mark点和极柱点的关系。焊接机定位到mark点…...
4.2 Prompt工程与任务建模:高效提示词设计与任务拆解方法
提示词工程(Prompt Engineering)和任务建模(Task Modeling)已成为构建高效智能代理(Agent)系统的核心技术。提示词工程通过精心设计的自然语言提示词(Prompts),引导大型语…...
oracle 锁的添加方式和死锁的解决
DML锁添加方式 DML 锁可由一个用户进程以显式的方式加锁,也可通过某些 SQL 语句隐含方式实现。 DML 锁有三种加锁方式:共享锁方式、独占锁方式、共享更新。 共享锁,独占锁用于 TM 锁,共享锁用于 TX 锁。 1)共享方式的表级锁 共享方…...
Nginx 二进制部署与 Docker 部署深度对比
一、核心概念解析 1. 二进制部署 通过包管理器(如 apt/yum)或源码编译安装 Nginx,直接运行在宿主机上。其特点包括: 直接性:与操作系统深度绑定,直接使用系统库和内核功能 。定制化:支持通过…...
以太网的mac帧格式
一.以太网的mac帧 帧的要求 1.长度 2.物理层...
每日算法-250424
每日算法打卡 (24/04/25) - LeetCode 2971 & 1647 记录一下今天解决的两道 LeetCode 题目 2971. 找到最大周长的多边形 题目 思路 贪心 一个基本的多边形构成条件是:最长边必须小于其他所有边的长度之和。 为了找到周长最大的多边形,我们应该尽可能…...
在本地部署n8n:完整指南
n8n是一个强大的工作流自动化工具,可以帮助你连接不同的应用程序和服务,无需编写复杂的代码。本指南将带你完成在本地计算机上部署n8n的完整过程。 什么是n8n? n8n(发音为"n-eight-n")是一个开源的工作流自…...
棋盘格角点检测顺序问题
文章目录 前言一、OpenCV函数测试二、原因分析三、libcbdetect修改总结 前言 棋盘格角点检测在相机拼接、机械臂手眼标定中等应用很广泛,通常也要求尽量各种角度摆放从而保证标定精度。然后就自然想到了这个问题:如果棋盘格任意角度摆放怎么能对应上角点…...
C++之类和对象:定义,实例化,this指针,封装
C语言是面向过程的,C是面向对象的,利用对象交互,接口完成事情。 类的定义: 我们在C语言中可以用struct创建自定义结构体,在C中可以在结构体中定义函数了,这种就被称为类。 #include<iostream> usi…...
Ubuntu系统下交叉编译iperf3
一、参考资料 Linux下iperf3移植到arm下测试100M网口-CSDN博客 Iperf3移植到ARM Linux及使用教程-CSDN博客 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编…...
游戏引擎学习第243天:异步纹理下载
仓库 https://gitee.com/mrxiao_com/2d_game_6 https://gitee.com/mrxiao_com/2d_game_5 回顾并为今天设定阶段 目前的开发工作主要回到了图形渲染相关的部分。我们之前写了自己的软件渲染器,这个渲染器性能意外地好,甚至可以以相对不错的帧率运行过场…...
27、Session有什么重⼤BUG?微软提出了什么⽅法加以解决?
Session的重大BUG 1、进程回收导致Session丢失 原理: IIS的进程回收机制会在系统繁忙、达到特定内存阈值等情况下,自动回收工作进程(w3wp.exe)。由于Session数据默认存储在进程内存中,进程回收时这些数据会被清除。 …...
机器学习在网络安全中的应用:守护数字世界的防线
一、引言 随着信息技术的飞速发展,网络安全问题日益凸显,成为全球关注的焦点。传统的网络安全防护手段,如防火墙、入侵检测系统(IDS)和防病毒软件,虽然在一定程度上能够抵御攻击,但在面对复杂多…...
从数据到智慧:解密机器学习的自主学习密码
在数字洪流奔涌的时代,每一次点击、每一行代码、每一条传感器数据都在生成海量信息。传统编程如同精心设计的齿轮组,需要工程师逐行编写规则;而机器学习则打破这一范式,赋予机器从数据中自主提炼规律、总结模式的超能力。这种能力…...
Trae或者VsCode无法识别相对路径(不自动切换工作目录)
在VsCode中或者Trae中,只要是在vscode的基础上修改得到的编辑器,都默认没有勾选自动选择当前文件路径为工作路径,因此需要手动修改工作路径或者设置,否则无法识别相对路径,PyCharm中就不会出现这种问题。 解决方法&…...
解决VSCode每次SSH连接服务器时,都需要下载vscode-server
如下图所示,本地下载或者在服务器终端上运行wget指令获得vscode服务器包 注意,解压完成后,需要修改文件名为你本地vscode的commit ID...
架构-系统工程与信息系统基础
一、系统工程核心知识 1. 系统工程定义 本质:一种组织管理技术,从整体出发分析系统要素(组成、结构、信息流、控制机制),追求“整体最优”,借助计算机实现规划、设计、管理、控制的优化。目标:…...
矩阵运算和线性代数操作开源库
用于矩阵运算和线性代数操作常用的开源库推荐,涵盖不同编程语言和硬件平台: C/C 库 Eigen 特点:高性能的模板库,支持矩阵/向量运算、线性求解、特征值计算等,无需依赖外部BLAS/LAPACK。 官网:https://eig…...
无标注文本的行业划分(行业分类)算法 —— 无监督或自监督学习
对于无标注文本的行业划分(行业分类),属于典型的无监督或自监督学习任务。以下是几种常见的算法方法及实现思路,适用于缺乏标注数据的场景: 一、基于关键词匹配的规则方法 核心思想:通过预定义的行业关键…...
电子病历高质量语料库构建方法与架构项目(计划篇)
电子病历(EMR)作为医疗信息化的重要产物,包含了丰富的医疗信息和临床知识,是辅助临床决策、药物挖掘和医学研究的重要资源。然而,电子病历数据具有非结构化、噪声大、专业性强等特点,如何构建高质量电子病历语料库成为医疗自然语言处理领域的核心挑战。本全计划将从项目背景…...
什么混合检索?在基于大模型的应用开发中,混合检索主要解决什么问题?
混合检索的定义 混合检索(Hybrid Retrieval)是一种结合多种检索技术优势的信息检索方法,旨在通过整合不同检索策略提升检索系统的准确性、召回率和适应性。其核心思想是将基于关键词的检索(如BM25、TF-IDF)与基于语义的检索(如向量检索、深度学习模型)相结合,以应对单…...
优化uniappx页面性能,处理页面滑动卡顿问题
问题:在页面遇到滑动特别卡的情况就是在页面使用了动态样式或者动态类,做切换的时候页面重新渲染导致页面滑动卡顿 解决:把动态样式和动态类做的样式切换改为通过获取元素修改样式属性值 循环修改样式示例 bannerList.forEach((_, index)…...
Yocto meta-toradex-security layer 创建独立数据分区
By Toradex 胡珊逢 简介 Toradex 为其产品使用的软件系统如 Linux 提供了诸多的安全功能,例如 Secure Boot、分区加密、OP-TEE 等,帮助用户应对安全合规。这些功能可以通过在 Yocto Project 中添加由 Toradex 开发的 meta-toradex-securitylayer 被轻松…...
uniapp 安卓离线本地打包,Android Studio生成apk包
第一步:HbuilderX生成本地资源包 下载最新的SDK 下载完后压缩下来是这样的 将HbuilderX生成的复制到这里,替换 Android Studio引入下载的最新文件里的HBuilder-Integrate-AS目录 好,接下来开始修改配置 把你的证书签名ÿ…...
C语言教程(十四):C 语言指针详解
一、指针的基本概念 指针是一个变量,其值为另一个变量的内存地址。简单来说,指针指向了内存中的某个位置,通过指针可以间接访问该位置存储的数据。指针的使用可以让程序更加高效地处理数据,特别是在处理数组、动态内存分配等方面。…...
2025年04月24日Github流行趋势
项目名称:markitdown 项目地址url:https://github.com/microsoft/markitdown项目语言:Python历史star数:53,351今日star数:822项目维护者:afourney, gagb, sugatoray, PetrAPConsulting, l-lumin项目简介&a…...
切割PDF使用python,库PyPDF2
使用 Python 将大型 PDF 文件分割成多个小文件 理解任务 将一个 170M 的 PDF 文件分割成多个 10M 左右的小文件。这在处理大型 PDF 文件时非常有用,例如: 减少单个文件的大小,方便传输或存储分别处理不同的文件部分提高 PDF 处理的效率 选…...
网络IP冲突的成因与解决方案
网络IP冲突的成因与解决方案 一、IP冲突的常见现象与危害二、IP冲突的常见原因三、6种实用解决方案四、预防IP冲突的4个最佳实践五、总结 前言 肝文不易,点个免费的赞和关注,有错误的地方请指出,看个人主页有惊喜。 作者:神的孩子…...
python版本得数独游戏
python版本得数独游戏 游戏说明: 游戏使用9x9数独棋盘,.表示可填写的空格 输入格式为行,列,数值(如3,5,7表示第3行第5列填7) 系统会自动检查以下内容: 输入格式是否正确 数字是否在1-9范围内 是否修改固定数字 是…...
64位系统上编译32位openh264 x264
在64位系统上要使用i386(32位库)的时候,有些是找不到apt可以安装的版本,所以需要手动编译安装,下面是openh264和x264的编译过程。 默认编译openh264 git clone https://github.com/cisco/openh264make ARCHi386 OSlinux PREFIX/lib/i386-li…...
加深对vector理解OJ题
17. 电话号码的字母组合 - 力扣(LeetCode) OJ(一)电话号码的字母组合 思路:这里以引用leetcode里面的一个大佬里面的图 1.这道题中,我们用递归的方法来写。 为了简洁展示,我们举例子”456“&am…...
协作开发攻略:Git全面使用指南 — 第三部分 特殊应用场景
协作开发攻略:Git全面使用指南 — 第三部分 特殊应用场景 Git 是一种分布式版本控制系统,用于跟踪文件和目录的变更。它能帮助开发者有效管理代码版本,支持多人协作开发,方便代码合并与冲突解决,广泛应用于软件开发领域…...
机器学习(9)——随机森林
文章目录 1. 随机森林的基本原理想2. 算法流程2.1. 数据采样(Bootstrap):2.2. 构建决策树:2.3. 聚合预测: 3. 随机森林的构建过程3.1. 数据集的随机抽样3.2. 决策树的训练3.3. 树的生长3.4. 多棵树的集成3.5. 输出预测…...
(第三篇)Springcloud之Ribbon负载均衡
一、简介 1、介绍 Spring Cloud Ribbon是Netflix发布的开源项目,是基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时&…...
Linux并发与竞争:从生活例子到内核实战
Linux并发与竞争:从生活例子到内核实战 一、并发与竞争:多车道公路的交通问题 想象一条四车道的高速公路(多核CPU),所有车辆(线程/进程)都想通过同一个收费站(共享资源)…...