【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)
目录
1 -> V2 -Echo Server多进程版本
2 -> V3 -Echo Server多线程版本
3 -> V3-1 -多线程远程命令执行
4 -> V4 -Echo Server线程池版本

1 -> V2 -Echo Server多进程版本
通过每个请求,创建子进程的方式来支持多连接。
InetAddr.hpp
#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>class InetAddr
{
public:InetAddr(struct sockaddr_in& addr) :_addr(addr){_port = ntohs(_addr.sin_port);_ip = inet_ntoa(_addr.sin_addr);}std::string Ip() { return _ip; }uint16_t Port() { return _port; };std::string PrintDebug(){std::string info = _ip;info += ":";info += std::to_string(_port); // "127.0.0.1:4444"return info;}const struct sockaddr_in& GetAddr(){return _addr;}bool operator == (const InetAddr & addr){//other codereturn this->_ip == addr._ip && this->_port == addr._port;}~InetAddr() {}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};
TcpSever.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, "create socket success,sockfd: % d\n", _listensock);// 2. 填充本地网络信息并 bindstruct 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);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success,sockfd: % d\n", _listensock);}// Tcp 连接全双工通信的.void Service(int sockfd){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer <<std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client quit...\n");break;}else{lg.LogMessage(Error, "read socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));break;}}}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v2 多进程pid_t id = fork();if (id < 0){close(sockfd);return;}else if (id == 0){// childclose(_listensock);if (fork() > 0)exit(0);InetAddr addr(peer); // 获取 client 地址信息lg.LogMessage(Info, "process connection: %s:%d\n",addr.Ip().c_str(), addr.Port());// 孙子进程,孤儿进程,被系统领养,正常处理Service(sockfd);close(sockfd);exit(0);}else{close(sockfd);pid_t rid = waitpid(id, nullptr, 0);if (rid == id){// do nothing}}}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n newsockfd: % d\n", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;
};
引入InetAddr.hpp,方便后面打印消息。
2 -> V3 -Echo Server多线程版本
Thread.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, "create socket success,sockfd: % d\n", _listensock);// 2. 填充本地网络信息并 bindstruct 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);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success,sockfd: % d\n", _listensock);}class ThreadData{public:ThreadData(int sockfd, struct sockaddr_in addr): _sockfd(sockfd), _addr(addr){}~ThreadData(){}public:int _sockfd;InetAddr _addr;};// Tcp 连接全双工通信的.// 新增 staticstatic void Service(ThreadData& td){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(td._sockfd, buffer, sizeof(buffer) -1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer <<std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(td._sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client[%s:%d] quit...\n",td._addr.Ip().c_str(), td._addr.Port());break;}else{lg.LogMessage(Error, "read socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));break;}}}static void* threadExcute(void* args){pthread_detach(pthread_self());ThreadData* td = static_cast<ThreadData*>(args);TcpServer::Service(*td);close(td->_sockfd);delete td;return nullptr;}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v3 多线程InetAddr addr(peer);pthread_t tid;ThreadData* td = new ThreadData(sockfd, peer);pthread_create(&tid, nullptr, threadExcute, (void*)td);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n newsockfd: % d\n", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;
};
使用最原始的接口,使用内部ThreadData类。
3 -> V3-1 -多线程远程命令执行
Command.hpp
- 命令类,用来执行命令,并获取结果。
- 这里暂停,做一个多线程的小业务。
#pragma once#include <iostream>
#include <string>
#include <set>
#include <unistd.h>class Command
{
private:
public:Command() {}Command(int sockfd) : _sockfd(sockfd){// 限定一下命令的个数和范围_safe_command.insert("ls");_safe_command.insert("pwd");_safe_command.insert("ls -l");_safe_command.insert("ll");_safe_command.insert("touch");_safe_command.insert("who");_safe_command.insert("whoami");}bool IsSafe(const std::string& command){auto iter = _safe_command.find(command);if (iter == _safe_command.end()) return false; // 要执行的命令不 set 中,不安全else return true;}std::string Execute(const std::string& command){if (!IsSafe(command)) return "unsafe";FILE* fp = popen(command.c_str(), "r");if (fp == nullptr)return std::string();char buffer[1024];std::string result;while (fgets(buffer, sizeof(buffer), fp)){result += buffer;}pclose(fp);return result;}std::string RecvCommand(){char line[1024];ssize_t n = recv(_sockfd, line, sizeof(line) - 1, 0); if (n > 0){line[n] = 0;return line;}else{return std::string();}}void SendCommand(std::string result){if (result.empty()) result = "done"; // 主要是有些命令没有结果,比如 touchsend(_sockfd, result.c_str(), result.size(), 0);}~Command(){}private:std::set<std::string> _safe_command;int _sockfd;std::string _command;
};
TcpSever.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "Command.hpp" // 引入命令执行const static int default_backlog = 6; // TODOclass TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, "create socket success,sockfd: % d\n", _listensock);// 2. 填充本地网络信息并 bindstruct 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);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success,sockfd: % d\n", _listensock);}class ThreadData{public:ThreadData(int sockfd, struct sockaddr_in addr): _sockfd(sockfd), _addr(addr){}~ThreadData(){}public:int _sockfd;InetAddr _addr;};// Tcp 连接全双工通信的.// 新增 staticstatic void Service(ThreadData& td){char buffer[1024];// 一直进行 IOwhile (true){Command command(td._sockfd);std::string commandstr = command.RecvCommand();if (commandstr.empty())return;std::string result = command.Execute(commandstr);command.SendCommand(result);}}static void* threadExcute(void* args){pthread_detach(pthread_self());ThreadData* td = static_cast<ThreadData*>(args);TcpServer::Service(*td);close(td->_sockfd);delete td;return nullptr;}void ProcessConnection(int sockfd, struct sockaddr_in& peer){// v3 多线程InetAddr addr(peer);pthread_t tid;ThreadData* td = new ThreadData(sockfd, peer);pthread_create(&tid, nullptr, threadExcute, (void*)td);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n newsockfd: % d\n", sockfd);ProcessConnection(sockfd, peer);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;
};
4 -> V4 -Echo Server线程池版本
引入系统部分的线程池,进行简单的业务处理。
TcpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"const static int default_backlog = 6; // TODOclass TcpServer : public nocopy
{
public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 创建 socket, file fd, 本质是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){lg.LogMessage(Fatal, "create socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Fatal);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR |SO_REUSEPORT, &opt, sizeof(opt));lg.LogMessage(Debug, "create socket success,sockfd: % d\n", _listensock);// 2. 填充本地网络信息并 bind// 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);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){lg.LogMessage(Fatal, "bind socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Bind_Err);}lg.LogMessage(Debug, "bind socket success, sockfd: %d\n",_listensock);// 3. 设置 socket 为监听状态,tcp 特有的if (listen(_listensock, default_backlog) != 0){lg.LogMessage(Fatal, "listen socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));exit(Listen_Err);}lg.LogMessage(Debug, "listen socket success,sockfd: % d\n", _listensock);}// Tcp 连接全双工通信的.void Service(int sockfd, InetAddr addr){char buffer[1024];// 一直进行 IOwhile (true){ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);if (n > 0){buffer[n] = 0;std::cout << "client say# " << buffer <<std::endl;std::string echo_string = "server echo# ";echo_string += buffer;write(sockfd, echo_string.c_str(),echo_string.size());}else if (n == 0) // read 如果返回值是 0,表示读到了文件结尾(对端关闭了连接!){lg.LogMessage(Info, "client[%s:%d] quit...\n",addr.Ip().c_str(), addr.Port());break;}else{lg.LogMessage(Error, "read socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));break;}}}void ProcessConnection(int sockfd, struct sockaddr_in& peer){using func_t = std::function<void()>;InetAddr addr(peer);func_t func = std::bind(&TcpServer::Service, this, sockfd,addr); // 这里不能 autoThreadPool<func_t>::GetInstance()->Push(func);}void Start(){_isrunning = true;while (_isrunning){// 4. 获取连接struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = accept(_listensock, CONV(&peer), &len);if (sockfd < 0){lg.LogMessage(Warning, "accept socket error, errnocode: % d, error string : % s\n", errno, strerror(errno));continue;}lg.LogMessage(Debug, "accept success, get n newsockfd: % d\n", sockfd);ProcessConnection(sockfd, peer);}_isrunning = false;}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;
};
感谢各位大佬支持!!!
互三啦!!!
相关文章:
【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)
目录 1 -> V2 -Echo Server多进程版本 2 -> V3 -Echo Server多线程版本 3 -> V3-1 -多线程远程命令执行 4 -> V4 -Echo Server线程池版本 1 -> V2 -Echo Server多进程版本 通过每个请求,创建子进程的方式来支持多连接。 InetAddr.hpp #pragma…...
面试高频问题:C/C++编译时内存五个分区
在面试时,C/C++编译时内存五个分区是经常问到的问题,面试官通过这个问题来考察面试者对底层的理解。在平时开发时,懂编译时内存分区,也有助于自己更好管理内存。 目录 内存分区的定义 内存分区的重要性 代码区 数据区 BSS区 堆区 栈区 静态内存分配 动态内存分配…...
阅读博士论文《功率IGBT模块健康状态监测方法研究》
IGBT的失效可以分为芯片级失效和封装级失效。其中封装级失效是IGBT模块老化的主要原因,是多种因素共同作用的结果。在DBC的这种结构中,流过芯片的负载电流通过键合线传导到 DBC上层铜箔,再经过端子流出模块。DBC与芯片和提供机械支撑的基板之…...
Spring ApplicationContext接口
ApplicationContext接口是Spring框架中更高级的IoC容器接口,扩展了BeanFactory接口,提供了更多的企业级功能。ApplicationContext不仅具备BeanFactory的所有功能,还增加了事件发布、国际化、AOP、资源加载等功能。 ApplicationContext接口的…...
[perl] 数组与哈希
数组变量以 符号开始,元素放在括号内 简单举例如下 #!/usr/bin/perl names ("a1", "a2", "a3");print "\$names[0] $names[0]\n"; print "size: ",scalar names,"\n";$new_names shift(names); …...
电机学习-SPWM原理及其MATLAB模型
SPWM原理及其MATLAB模型 一、SPWM原理二、基于零序分量注入的SPWM三、MATLAB模型 一、SPWM原理 SPWM其实是相电压的控制方式,定义三相正弦相电压的表达式: { V a m V m sin ω t V b m V m sin ( ω t − 2 3 π ) V c m V m sin ( ω t 2…...
群控系统服务端开发模式-应用开发-腾讯云上传工厂及七牛云上传工厂开发
记住业务流程图,要不然不清楚自己封装的是什么东西。 一、腾讯云工厂开发 切记在根目录下要安装腾讯云OSS插件,具体代码如下: composer require qcloud/cos-sdk-v5 在根目录下extend文件夹下Upload文件夹下channel文件夹中,我们修…...
【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法
【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法 【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法 文章目录 【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和…...
《计算机原理与系统结构》学习系列——处理器(下)
系列文章目录 目录 流水线冒险数据冒险数据相关与数据冒险寄存器先读后写旁路取数使用型冒险阻塞 控制冒险分支引发的控制冒险假设分支不发生动态分支预测双预测位动态分支预测缩短分支延迟带冒险控制的单周期流水线图 异常MIPS中的异常MIPS中的异常处理另一种异常处理机制非精…...
JDK新特性(8-21)数据类型-直接内存
目录 Jdk 新特性 JDK 8 特性 默认方法实现作用:可以使接口更加灵活,不破坏现有实现的情况下添加新的方法。 函数式接口 StreamAPI JDK 9 特性 JDK 10 特性 JDK 11 特性 JDK 14 特性 JDK 17 特性 JDK 21 特性 数据类型 基本数据类型和引用数据类型的区别…...
003-Kotlin界面开发之声明式编程范式
概念本源 在界面程序开发中,有两个非常典型的编程范式:命令式编程和声明式编程。命令式编程是指通过编写一系列命令来描述程序的运行逻辑,而声明式编程则是通过编写一系列声明来描述程序的状态。在命令式编程中,程序员需要关心程…...
QT pro项目工程的条件编译
QT pro项目工程的条件编译 前言 项目场景:项目中用到同一型号两个相机,同时导入两个版本有冲突,编译不通过, 故从编译就区分相机导入调用,使用宏区分 一、定义宏 在pro文件中定义宏: DEFINES USE_Cam…...
深度学习之经典网络-AlexNet详解
AlexNet 是一种经典的卷积神经网络(CNN)架构,在 2012 年的 ImageNet 大规模视觉识别挑战赛(ILSVRC)中表现优异,将 CNN 引入深度学习的新时代。AlexNet 的设计在多方面改进了卷积神经网络的架构,…...
部署Prometheus、Grafana、Zipkin、Kiali监控度量Istio
1. 模块简介 Prometheus 是一个开源的监控系统和时间序列数据库。Istio 使用 Prometheus 来记录指标,跟踪 Istio 和网格中的应用程序的健康状况。Grafana 是一个用于分析和监控的开放平台。Grafana 可以连接到各种数据源,并使用图形、表格、热图等将数据…...
结合 Spring Boot Native 和 Spring Boot 构建高性能服务器架构
随着云计算和微服务架构的普及,开发者们不断寻求提高应用性能和用户体验的解决方案。Spring Boot Native 的出现,利用 GraalVM 的原生映像特性,使得 Java 应用的启动速度和资源占用得到了显著改善。本文将深入探讨如何将前端应用使用 Spring …...
ArcGIS影像调色(三原色)三原色调整
本期主要介绍ArcGIS影像调色(三原色) ArcGIS影像调色(三原色),对比度、亮度、gamma。红绿蓝三原色调整。 视频学习 ArcGIS影像调色(三原色)...
SQLite从入门到精通面试题及参考答案
目录 SQLite 是什么? SQLite 的优点有哪些? 轻量级与易于部署 零配置和低维护成本 良好的兼容性和跨平台性 高性能和可靠性 SQLite 的局限性有哪些? 并发处理能力有限 缺乏用户管理和权限控制功能 有限的扩展性 有限的网络支持 SQLite 和其他数据库系统(如 MyS…...
【C/C++】字符/字符串函数(0)(补充)——由ctype.h提供
零.导言 除了字符分类函数,字符转换函数也是一类字符/字符串函数。 C语言提供了两种字符转换函数,分别是 toupper , tolower。 一.什么是字符转换函数? 顾名思义,即转换字符的函数,如大写字母转小写字母&am…...
Git 的特殊配置文件
文章目录 1.前言2.Git 标准配置文件.gitignore作用格式示例 .gitattributes作用格式示例 .gitmodules作用格式示例 .gitconfig作用格式示例 3.非 Git 标准约定文件.gitkeep简介示例 .gitacls作用格式示例 参考文献 1.前言 Git 是一个强大的版本控制系统,它使用多个…...
数据的表现形式(1)
数据的表现形式 西文字符编码 ASCII码(美国信息交换标准码) 计算机内部用一个字节(8位二进制),来存放一个7位ASCII码,最高位为“0”,共 可以表示128个不同字符 ASCII码中,0是48&…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
