当前位置: 首页 > news >正文

计算机网络socket编程(6)_TCP实网络编程现 Command_server

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

计算机网络socket编程(6)_TCP实网络编程现 Command_server

收录于专栏【计算机网络】
本专栏旨在分享学习计算机网络的一点学习笔记,欢迎大家在评论区交流讨论💌 
  

目录

功能介绍 

1. InetAddr.hpp

2. LockGuard.hpp

3. Log.hpp

4. Command.hpp

5. TcpServer.hpp

6. TcpServerMain.cc

7. TcpClientMain.cc


功能介绍 

和上回 TCP 网络编程一样, 实现简单的 Command_server

还有就是网络编程代码真的是又多又杂, 有的时候我自己都烦, 没办法网络部分就是这样的, 我最近会尽快更完这个 socket 编程, 提早进入概念部分, 一直编程感觉少了什么~ 还得跟概念结合起来看, 感兴趣的宝子们不要忘记了点赞关注哦! 我现在在网络部分真的待不了一点, 希望我能尽快挣脱网络, 更新数据库 MySQL 的东西吧!  

1. InetAddr.hpp

 老演员了, InetAddr 类封装了 IP 地址和端口号,提供了转换网络字节序和主机字节序的方法,支持比较、获取 IP 地址、端口号、地址字符串等功能。这个类的设计是典型的用于网络编程,特别是在处理 IPv4 地址时非常有用。

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void ToHost(const struct sockaddr_in &addr){_port = ntohs(addr.sin_port);// _ip = inet_ntoa(addr.sin_addr);char ip_buf[32];// inet_p to n// p: process// n: net// inet_pton(int af, const char *src, void *dst);// inet_pton(AF_INET, ip.c_str(), &addr.sin_addr.s_addr);::inet_ntop(AF_INET, &addr.sin_addr, ip_buf, sizeof(ip_buf));_ip = ip_buf;}public:InetAddr(const struct sockaddr_in &addr):_addr(addr){ToHost(addr);}InetAddr(){}bool operator == (const InetAddr &addr){return (this->_ip == addr._ip && this->_port == addr._port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}struct sockaddr_in Addr(){return _addr;}std::string AddrStr(){return _ip + ":" + std::to_string(_port);}~InetAddr(){}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};

2. LockGuard.hpp

老演员 +1, LockGuard 类是一个封装了互斥锁的RAII对象,提供了简化的锁管理功能。它确保在对象生命周期结束时自动释放锁,从而避免了忘记解锁和潜在的死锁问题。它特别适用于多线程程序中对共享资源的保护,能显著提高代码的可读性和可靠性。 

#pragma once#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};

3. Log.hpp

 老演员+1, 这段代码是一个 C++ 日志系统的实现,它提供了日志记录功能,可以将日志输出到屏幕或写入文件。系统使用了互斥锁(pthread_mutex_t)来保证日志输出的线程安全,并且提供了多种日志级别(DEBUG, INFO, WARNING, ERROR, FATAL)。

#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include "LockGuard.hpp"namespace log_ns
{enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level){switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetCurrTime(){time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class logmessage{public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;// log.logMessage("", 12, INFO, "this is a %d message ,%f, %s hellwrodl", x, , , );class Log{public:Log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile, std::ios::app);if (!out.is_open())return;char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());out.write(logtxt, strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){// 加过滤逻辑 --- TODOLockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void logMessage(std::string filename, int filenumber, int level, const char *format, ...){logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber = filenumber;lg._curr_time = GetCurrTime();va_list ap;va_start(ap, format);char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);va_end(ap);lg._message_info = log_info;// 打印出来日志FlushLog(lg);}~Log(){}private:int _type;std::string _logfile;};Log lg;#define LOG(Level, Format, ...)                                        \do                                                                 \{                                                                  \lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \} while (0)
#define EnableScreen()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
};

4. Command.hpp

这个代码实现了一个简单的命令执行器,允许客户端通过网络发送命令并执行,同时对命令进行安全检查,确保执行的是在白名单中的安全命令。 

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <set>
#include "Log.hpp"
#include "InetAddr.hpp"class Command
{
public:Command(){// 白名单_safe_command.insert("ls");_safe_command.insert("touch"); // touch filename_safe_command.insert("pwd");_safe_command.insert("whoami");_safe_command.insert("which"); // which pwd}~Command() {}// ls;rm -rf /bool SafeCheck(const std::string &cmdstr){for(auto &cmd : _safe_command){if(strncmp(cmd.c_str(), cmdstr.c_str(), cmd.size()) == 0){return true;}}return false;}std::string Excute(const std::string &cmdstr){if(!SafeCheck(cmdstr)){return "unsafe";}std::string result;FILE *fp = popen(cmdstr.c_str(), "r");if(fp){char line[1024];while(fgets(line, sizeof(line), fp)){result += line;}return result.empty() ? "success" : result;}return "execute error";}void HandlerCommand(int sockfd, InetAddr addr){// 我们把他当做一个长服务while (true){char commandbuf[1024]; // 当做字符串, ls -a -l -> os --> ls -ssize_t n = ::recv(sockfd, commandbuf, sizeof(commandbuf) - 1, 0); // TODOif (n > 0){commandbuf[n] = 0;LOG(INFO, "get command from client %s, command: %s\n", addr.AddrStr().c_str(), commandbuf);std::string result = Excute(commandbuf);::send(sockfd, result.c_str(), result.size(), 0);}else if (n == 0){LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());break;}else{LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());break;}}}
private:std::set<std::string> _safe_command;
};

Command 构造函数

构造函数初始化时,构建了一个白名单 safe_command,该白名单包括允许执行的命令。只有这些命令可以在后续的调用中被安全地执行。

"ls":列出目录内容。

"touch":创建空文件(命令格式如 touch filename)。

"pwd":显示当前工作目录。

"whoami":显示当前用户。

"which":显示命令的完整路径。

这些命令一般是无害的,可以允许远程执行。其他的命令(例如 rm -rf /)则会被拒绝。

SafeCheck 函数

这个函数用于检查命令是否安全。它遍历白名单中的每个命令,使用 strncmp 判断是否有命令前缀匹配。如果有匹配的命令前缀(例如 ls -l),则认为这个命令是安全的,返回 true,否则返回 false。

strncmp 的作用:strncmp 比较两个字符串的前 n 个字符,cmd.size() 是匹配的字符长度,因此如果 cmdstr 以白名单命令的某个前缀开始,它将被认为是一个安全命令

Excute 函数

这个函数用于执行实际的命令。首先,它会调用 SafeCheck 函数来确保命令是安全的。如果命令不在白名单中,返回 "unsafe"。

如果命令安全,它通过 popen 调用操作系统的命令行执行命令,并读取命令输出:

popen(cmdstr.c_str(), "r") 会执行命令,并返回一个文件指针,可以通过它读取命令的标准输出。

使用 fgets 一行一行地读取命令输出,直到没有输出为止。

最后将结果返回。如果命令有输出,则返回输出内容;如果命令没有输出,则返回 "success"。

如果执行命令时发生错误(例如,popen 失败),返回 "execute error"。

HandlerCommand 函数

这个函数处理与客户端的交互。它是一个长连接服务,持续接收和处理客户端发送的命令。

工作流程:

使用 recv 函数接收来自客户端的命令,存储在 commandbuf 中。recv 会返回接收到的字节数,如果接收到有效数据(n > 0),继续处理。

通过 LOG(INFO, ...) 记录从客户端接收到的命令。

使用 Excute 函数执行命令并获取执行结果。

使用 send 将命令的执行结果返回给客户端。

如果接收到的数据为 0(即客户端关闭连接),则记录日志并结束循环。

如果发生读取错误(n < 0),记录错误日志并结束循环。

这个方法基本上是一个长连接的服务器循环,不断接收和执行命令,直到客户端断开连接或发生错误。

5. TcpServer.hpp

这段代码实现了一个基本的 TCP 服务器 类 (TcpServer),通过多线程处理客户端的连接。 

#pragma once
#include <iostream>
#include <cstring>
#include <functional>
#include <unistd.h>
#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 "InetAddr.hpp"using namespace log_ns;enum
{SOCKET_ERROR = 1,BIND_ERROR,LISTEN_ERR
};const static int gport = 8888;
const static int gsock = -1;
const static int gblcklog = 8;using command_service_t = std::function<void(int sockfd, InetAddr addr)>;class TcpServer
{
public:TcpServer(command_service_t service, uint16_t port = gport): _port(port),_listensockfd(gsock),_isrunning(false),_service(service){}void InitServer(){// 1. 创建socket_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_listensockfd < 0){LOG(FATAL, "socket create error\n");exit(SOCKET_ERROR);}LOG(INFO, "socket create success, sockfd: %d\n", _listensockfd); // 3struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// 2. bind sockfd 和 Socket addrif (::bind(_listensockfd, (struct sockaddr *)&local, sizeof(local)) < 0){LOG(FATAL, "bind error\n");exit(BIND_ERROR);}LOG(INFO, "bind success\n");// 3. 因为tcp是面向连接的,tcp需要未来不断地能够做到获取连接if (::listen(_listensockfd, gblcklog) < 0){LOG(FATAL, "listen error\n");exit(LISTEN_ERR);}LOG(INFO, "listen success\n");}class ThreadData{public:int _sockfd;TcpServer *_self;InetAddr _addr;public:ThreadData(int sockfd, TcpServer *self, const InetAddr &addr):_sockfd(sockfd), _self(self), _addr(addr){}};void Loop(){// signal(SIGCHLD, SIG_IGN);_isrunning = true;while (_isrunning){struct sockaddr_in client;socklen_t len = sizeof(client);// 4. 获取新连接int sockfd = ::accept(_listensockfd, (struct sockaddr *)&client, &len);if (sockfd < 0){LOG(WARNING, "accept error\n");continue;}InetAddr addr(client);LOG(INFO, "get a new link, client info : %s, sockfd is : %d\n", addr.AddrStr().c_str(), sockfd);// version 2 ---- 多线程版本 --- 不能关闭fd了,也不需要了pthread_t tid;ThreadData *td = new ThreadData(sockfd, this, addr);pthread_create(&tid, nullptr, Execute, td); // 新线程进行分离}_isrunning = false;}static void *Execute(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);td->_self->_service(td->_sockfd, td->_addr);::close(td->_sockfd);delete td;return nullptr;}// void Service(int sockfd, InetAddr addr)// {//     // 长服务//     while (true)//     {//         char inbuffer[1024]; // 当做字符串//         ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);//         if (n > 0)//         {//             inbuffer[n] = 0;//             LOG(INFO, "get message from client %s, message: %s\n", addr.AddrStr().c_str(), inbuffer);//             std::string echo_string = "[server echo] #";//             echo_string += inbuffer;//             write(sockfd, echo_string.c_str(), echo_string.size());//         }//         else if (n == 0)//         {//             LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());//             break;//         }//         else//         {//             LOG(ERROR, "read error: %s\n", addr.AddrStr().c_str());//             break;//         }//     }// }~TcpServer() {}private:uint16_t _port;int _listensockfd;bool _isrunning;command_service_t _service;
};

常量定义

定义了一些常量,用于表示不同阶段可能发生的错误类型(如 SOCKET_ERROR, BIND_ERROR, LISTEN_ERR

gport:默认的服务器端口号 8888。

gsock:默认的套接字文件描述符值,表示未初始化的套接字。

gblcklog:listen() 系统调用的 backlog(最大连接数),即服务器在开始接收连接之前,内核的连接队列最大容量。

command_service_t 类型定义

定义了一个类型别名 command_service_t,它是一个 std::function 类型,表示一个接受两个参数(sockfd 和 addr)并返回 void 的函数。这个函数在实际的应用中将用于处理每个客户端的连接请求。

TcpServer 类构造函数

command_service_t service:构造函数接收一个处理客户端请求的回调函数 service,即每当一个客户端连接建立后,服务器就调用该函数来处理连接。

uint16_t port:默认端口为 8888,服务器将监听该端口。

初始化服务器 (InitServer 方法)

创建 Socket:使用 ::socket() 创建一个 TCP 套接字 (SOCK_STREAM)。

如果创建失败(socket 返回值小于 0),日志记录错误并退出程序。

绑定 Socket 地址:通过 ::bind() 将创建的套接字与指定的端口和 IP 地址绑定。

服务器绑定到本机所有可用 IP 地址 (INADDR_ANY),并监听指定的端口。

监听连接:通过 ::listen() 开始监听传入的连接,gblcklog 参数指定了等待连接的最大队列长度。

ThreadData 类

ThreadData:用于保存每个客户端连接的信息,包含:

_sockfd:客户端的套接字文件描述符。

_self:指向当前 TcpServer 实例的指针。

_addr:客户端的地址信息(封装在 InetAddr 对象中)。

Loop 方法

Loop:这是服务器的主循环,主要功能是:

使用 ::accept() 接受新的客户端连接。

每当接收到一个新连接时,使用 pthread_create 创建一个新线程,线程通过 Execute 函数处理客户端的请求。

每个连接的信息(套接字和客户端地址)封装在 ThreadData 中传递给线程。

pthread_create:创建一个新线程来处理客户端的请求。线程通过 Execute 方法执行指定的服务函数。

Execute 方法

Execute:线程的入口函数,执行以下操作:

使用 pthread_detach(pthread_self()) 将线程设为分离状态,确保线程结束后自动回收资源。

调用传入的回调函数 td->_self->_service(td->_sockfd, td->_addr),即执行传递给服务器的具体业务逻辑。

关闭客户端连接的套接字。

删除 ThreadData 对象。

6. TcpServerMain.cc

#include "TcpServer.hpp"
#include "Command.hpp"#include <memory>// ./tcpserver 8888
int main(int argc, char *argv[])
{if (argc != 2){std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);Command cmdservice;std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(std::bind(&Command::HandlerCommand,&cmdservice, std::placeholders::_1,std::placeholders::_2),port);tsvr->InitServer();tsvr->Loop();return 0;
}

7. TcpClientMain.cc

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>// ./tcpclient server-ip server-port
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 1. 创建socketint sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}// 注意:不需要显示的bind,但是一定要有自己的IP和port,所以需要隐式的bind,OS会自动bind sockfd,用自己的IP和随机端口号// 什么时候进行自动bind?If the connection or binding succeedsstruct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);::inet_pton(AF_INET, serverip.c_str(), &server.sin_addr);int n = ::connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cerr << "connect socket error" << std::endl;exit(2);}while(true){std::string message;std::cout << "Enter #";std::getline(std::cin, message);write(sockfd, message.c_str(), message.size());char echo_buffer[1024];n = read(sockfd, echo_buffer, sizeof(echo_buffer));if(n > 0){echo_buffer[n] = 0;std::cout << echo_buffer << std::endl;}else{break;}}::close(sockfd);return 0;
}

 效果展示 : 

虽然命令有些少, 但是整体还是没有问题的~ 

相关文章:

计算机网络socket编程(6)_TCP实网络编程现 Command_server

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 计算机网络socket编程(6)_TCP实网络编程现 Command_server 收录于专栏【计算机网络】 本专栏旨在分享学习计算机网络的一点学习笔记&#xff0c;欢迎大家在评论…...

RabbitMQ 集群

文章目录 前言单机多节点搭建集群创建RabbitMQ节点停止服务并重置将节点加入到集群中查看集群状态宕机 结论 前言 RabbitMQ 作为消息中间件&#xff0c;可以与多个生产者和消费者进行绑定&#xff0c;但是如何只有一台 RabbitMQ 服务的话&#xff0c;那么这个 RabbitMQ 就需要…...

从零开始:使用 Spring Boot 开发图书管理系统

如何利用是springboot搭建一个简单的图书管理系统&#xff0c;下面让我们一起来看看吧 文章目录 项目结构1. 主类 LibraryApplication.java功能与注意事项&#xff1a; 2. 模型类 Book.java功能与注意事项&#xff1a; 3. 数据仓库接口 BookRepository.java功能与注意事项&…...

速盾:海外服务器使用CDN加速有什么优势?

CDN&#xff08;Content Delivery Network&#xff09;是指一种分布式网络架构&#xff0c;将内容分发到全球多个节点服务器上&#xff0c;使用户能够就近获取所需内容。海外服务器使用CDN加速&#xff0c;具有以下几个优势&#xff1a; 提高访问速度&#xff1a;CDN将内容复制…...

(二)手势识别——动作模型训练【代码+数据集+python环境(免安装)+GUI系统】

&#xff08;二&#xff09;手势识别——动作模型训练【代码数据集python环境&#xff08;免安装&#xff09;GUI系统】 背景意义 随着互联网的普及和机器学习技术的进一步发展&#xff0c;手势识别技术开始使用深度学习等方法进行手势识别&#xff0c;如Convolutional Neural…...

window系统下使用open-webui+ollama部署大模型

前面一篇博文中讲述了window下用ollama+AnythingLLM部署本地知识库,但是个人感觉anythingllm不是很好用,还不如直接用cmd窗口,而且仅能本机使用,如果想部署到服务器上面供其他人访问,完全不可行,但是使用open-webui+ollama或者独立的open-webui救可以实现。 使用open-web…...

一加ACE 3 Pro手机无法连接电脑传输文件问题

先说结论&#xff1a;OnePlus手机无法连接电脑传输数据的原因&#xff0c;大概率是一加数据线的问题。尝试其他手机品牌的数据线&#xff08;比如华为&#xff09;&#xff0c;再次尝试。 连接电脑方法&#xff1a; 1 打开开发者模式&#xff08;非必要操作&#xff09; 进入…...

因果机器学习EconML | 客户细分案例——基于机器学习的异质性处理效果估计

机器学习的最大承诺之一是在众多应用领域中实现决策自动化。在大多数数据驱动的个性化决策场景中出现的一个核心问题是对异质性处理效果的估计&#xff1a;作为处理样本的一组可观察特征的函数&#xff0c;干预对感兴趣结果的影响是什么&#xff1f;例如&#xff0c;这个问题出…...

找到最大“葫芦”组合

文章目录 问题描述解题思路分析1. 数据预处理2. 特殊情况处理3. 普通情况计算4. 结果输出 Java代码实现复杂度分析与优化 在经典德州扑克中&#xff0c;“葫芦”是一种较强的牌型。它由五张牌组成&#xff0c;其中三张牌面值相同&#xff0c;另外两张牌面值也相同。本文将探讨一…...

shell(9)完结

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…...

【计算机网络】多路转接之select

系统提供select()来实现多路转接 IO 等 拷贝 -> select()只负责等待&#xff0c;可以一次等待多个fd select()本身没有数据拷贝的能力&#xff0c;拷贝要read()/write()来完成 一、select的使用 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exc…...

数据库-基础理论

文章目录 前言一、ORM框架二、ACID原则三、事务Transaction四、N1问题五、Normalization三范式六、FMEA方法论&#xff08;Failure Mode and Effects Analysis&#xff09;七、Profiling和PerformanceSchema查询分析 前言 基础理论 ORM框架、ACID原则、事务Transaction、N1问…...

Linux——1_系统的延迟任务及定时任务

系统的延迟任务及定时任务 在系统中我们的维护工作大多数时在服务器行对闲置时进行 我们需要用延迟任务来解决自动进行的一次性的维护 延迟任务时一次性的&#xff0c;不会重复执行 当延迟任务产生输出后&#xff0c;这些输出会以邮件的形式发送给延迟任务发起者 在RHEL9中…...

C++ 矩阵旋转

【问题描述】 编写一个程序&#xff0c;读入一个矩阵&#xff0c;输出该矩阵以第一行第一列数字为中心&#xff0c;顺时针旋转90度后的新矩阵&#xff0c;例如&#xff1a; 输入的矩阵为: 1 2 3 4 5 6 顺时针旋转90度后输出的矩阵为&#xff1a; 4 1 5 2 6 3 【输入…...

Docker学习笔记整理

这周不知道写点啥内容做个分享&#xff0c;但还是秉持学会分享的精神&#xff0c;粗略放一些Docker相关的问题和解答吧&#xff0c;后面有机会再补补再深挖深挖o(>﹏<)o 1. 容器VS虚拟机 虚拟机是一种带环境安装的解决方案&#xff08;资源完全隔离&#xff09;,有以下缺…...

计算机组成原理期末试题三(含答案)

本科生期末试卷 三 一&#xff0e;选择题&#xff08;每小题1分&#xff0c;共10分&#xff09; 1&#xff0e;冯诺依曼机工作的基本方式的特点是______。 A 多指令流单数据流 B 按地址访问并顺序执行指令 C 堆栈操作 D 存贮器按内容选择地址 2&#xff0e;在机器数______中&a…...

django+boostrap实现注册

一、django介绍 Django 是一个高级的 Python 网络框架&#xff0c;可以快速开发安全和可维护的网站。由经验丰富的开发者构建&#xff0c;Django 负责处理网站开发中麻烦的部分&#xff0c;因此你可以专注于编写应用程序&#xff0c;而无需重新开发。 它是免费和开源的&#x…...

C++初阶——类和对象(下)

目录 1、再探构造函数——初始化列表 2、类型转换 3、static成员 4、友元 5、内部类 6、匿名对象 7、对象拷贝时编译器的优化(了解) 1、再探构造函数——初始化列表 1. 构造函数初始化除了使用函数体内赋值&#xff0c;还有一种方式——初始化列表&#xff0c; 初始化列…...

趋势洞察|AI 能否带动裸金属 K8s 强势崛起?

随着容器技术的不断成熟&#xff0c;不少企业在开展私有化容器平台建设时&#xff0c;首要考虑的问题就是容器的部署环境——是采用虚拟机还是物理机运行容器&#xff1f;在往期“虚拟化 vs. 裸金属*”系列文章中&#xff0c;我们分别对比了容器部署在虚拟化平台和物理机上的架…...

idea初始化设置

下载idea&#xff1a; https://www.jetbrains.com/idea/ 安装idea 安装插件&#xff1a; Rainbow BracketsLombokMybatisXSonarLintMaven HelperCodeGeeX&#xff08;国内AI插件可用&#xff09; 设置idea注释模板&#xff1a; 设置代码注释模板&#xff1a; https://blo…...

LINUX系统编程之——环境变量

目录 环境变量 1、基本概念 2、查看环境变量的方法 三、查看PATH环境变量的內容 1&#xff09;不带路径也能运行的自己的程序 a、将自己的程序直接添加到PATH指定的路径下 b、将程序所在的路径添加到PATH环境中 四、环境变量与本地变量 1、本地变量创建 2、环境变量创…...

健康老龄化:适合老年人的播客

什么是播客 什么是播客&#xff1f;好问题。对于那些还不熟悉这个术语的人来说&#xff0c;播客有点像在线广播或电视节目。这是一个可下载、可流式传输的程序&#xff0c;定期发布剧集&#xff0c;时长从几分钟到一个多小时不等。您可以在计算机、智能手机或平板电脑上…...

家庭智慧工程师:如何通过科技提升家居生活质量

在今天的数字化时代&#xff0c;家居生活已经不再只是简单的“住”的地方。随着物联网&#xff08;IoT&#xff09;、人工智能&#xff08;AI&#xff09;以及自动化技术的快速发展&#xff0c;越来越多的家庭开始拥抱智慧家居技术&#xff0c;将他们的家变得更加智能化、便捷和…...

Milvus概念

非结构化数据、嵌入和 Milvus 非结构化数据&#xff08;如文本、图像、音频&#xff09;格式多样&#xff0c;蕴含丰富的语义信息&#xff0c;使其分析变得复杂。为了管理这种复杂性&#xff0c;嵌入技术被用来将非结构化数据转换为数值向量&#xff0c;这些向量能够捕捉数据的…...

为什么调用 setState 而不是直接改变 state

在React中&#xff0c;调用setState方法而不是直接改变state的原因涉及多个方面&#xff0c;包括性能优化、状态管理的可预测性、React的设计理念等。以下是对这些原因的详细解释&#xff1a; 1. 性能优化 异步更新与批量处理&#xff1a;setState是异步执行的&#xff0c;Rea…...

【Python爬虫五十个小案例】爬取豆瓣电影Top250

博客主页&#xff1a;小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介&#xff1a;分享五十个Python爬虫小案例 &#x1fab2;前言 在这篇博客中&#xff0c;我们将学习如何使用Python爬取豆瓣电影Top250的数据。我们将使用requests库来发送HTTP请求&#xff0c;…...

cocos creator 3.8 物理碰撞器Collider+刚体RigidBody 8

遇到一个朋友&#xff0c;你来就行的朋友&#xff0c;我过去了&#xff0c;管吃管住&#xff0c;这样的朋友真的很难求。 最近离职了&#xff0c;很难想象&#xff0c;一份策划书一天能给你改n次&#xff0c;一周能郁闷&#xff0c;上一个功能没搞完&#xff0c;让你搞下一个功…...

Python爬取豆瓣电影全部分类数据并存入数据库

在当今数字化的时代&#xff0c;网络上丰富的影视资源信息吸引着众多开发者去挖掘和利用。今天&#xff0c;我就来和大家分享一段有趣的代码&#xff0c;它能够从豆瓣电影平台获取相关数据并存储到数据库中哦。 结果展示&#xff08;文末附完整代码&#xff09;&#xff1a; 目…...

算法模板1:排序+二分+高精度+前缀+差分

文章目录 1.1 排序STL sort函数快速排序算法模板归并排序算法模板 1.2 二分整数二分算法模板浮点数二分算法模板 1.3 高精度高精度加法高精度减法高精度乘低精度高精度除以低精度 1.4 前缀和与差分**一维前缀和****二维前缀和****一维差分****二维差分** 之前整理了好多算法模板…...

SpringBoot实现单文件上传

一、在springBoot项目中的pom.xml添加依赖。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> 二、在资源目录下的static目录下中创建一个upload.html的表单文件…...