【C++项目】从零实现一个在线编译器

在这里插入图片描述

前言

身为一名程序员,想必大家都有接触过像leetcode这样的刷题网站,不知你们在刷题的过程中是否思考过一个问题:它们是如何实现在线编译运行的功能。如果你对此感到好奇,那么本文将一步步带你来实现一个简易在线编译器。

项目概述

项目的基本逻辑:前端用户在网页上输入代码与参数,后端通过多进程的方式来编译运行代码,然后将标准输出、标准错误信息返回给前端页面。

前后端交互数据格式

// 前端发送
{"code": "代码","cpu_limit": "CPU限制","mem_limit": "内存限制"
}// 后端发送
{"reason": "错误原因","status": "状态码","stderr": "错误输出","stdout": "标准输出"
}

使用的第三方库

后端:

  • cpp-httplib:用于处理HTTP请求和响应。

  • jsoncpp:用于解析和生成JSON数据。

  • spdlog:用于日志记录。

前端:

  • jquery:简化JavaScript操作,方便进行DOM操作和Ajax请求。

  • ace:提供代码编辑器功能,支持语法高亮和代码自动完成。

运行效果

具体实现

后端逻辑

后端分为编译模块和运行模块,均使用多进程的方式来运行,并根据用户所选语言的语言来选择不同的编译器和运行方式。后端代码分为四部分:公共模块(工具类)、编译模块、运行模块、编译运行模块(整合编译与运行)。

公共模块

日记类

日记系统对spdlog进行了最低程度的封装,实现了单例日记系统,并定义宏来简化其使用。

//
// Created by lang liu on 24-4-23.
//#pragma once#ifdef DEBUG
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#endif#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>namespace ns_log {//TODO 初始化日记 完善class Log {public:static Log &getInstance() {std::call_once(_flag, []() {_instance = new Log();});return *_instance;}auto getLogger()->std::shared_ptr<spdlog::logger>{return _logger;}private:Log() {_logger = spdlog::stdout_color_mt("nil");_logger->set_level(spdlog::level::debug);_logger->set_pattern("[%^%l%$] [%Y-%m-%d %H:%M:%S] [%t] [%s:%#] %v");}~Log() {spdlog::drop_all();}private:static std::once_flag _flag;static Log *_instance;std::shared_ptr<spdlog::logger> _logger;};std::once_flag Log::_flag;Log *Log::_instance = nullptr;#define LOG_DEBUG(...)    SPDLOG_LOGGER_DEBUG(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_INFO(...)     SPDLOG_LOGGER_INFO(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_WARN(...)     SPDLOG_LOGGER_WARN(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_ERROR(...)    SPDLOG_LOGGER_ERROR(Log::getInstance().getLogger(), __VA_ARGS__)
#define LOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(Log::getInstance().getLogger(), __VA_ARGS__)
}// #else
//[x] 无spdlog
// #include <iostream>
// #include <format>
// #include "util.hpp"// namespace ns_log {//     using namespace ns_util;//     enum {
//         INFO,
//         DEBUG,
//         WARN,
//         ERROR,
//         CRITICAL
//     };//     inline std::ostream &Log(const std::string &level, const std::string &str) {
//         std::string msg = std::format("[{}] [{}] [{}:{}] {}", level, TimeUtil::GetTimeStamp(), __FILE__, __LINE__,
//                                       str);
//         // auto ret = __FILE_NAME__;
//         return std::cout << msg;
//     }// #define LOG_INFO(...)     Log("INFO", __VA_ARGS__)
// #define LOG_DEBUG(...)    Log("DEBUG", __VA_ARGS__)
// #define LOG_WARN(...)     Log("WARN", __VA_ARGS__)
// #define LOG_ERROR(...)    Log("ERROR", __VA_ARGS__)
// #define LOG_CRITICAL(...) Log("CRITICAL", __VA_ARGS__)// }// #endif

工具类

工具类分为时间工具、文件工具、路径工具。

时间工具:时间工具类用于生成时间戳,辅助文件工具生成唯一的文件名(UUID)。

文件工具:用于实现读写、创建、删除文件。

路径工具:用于更改文件的后缀。

//
// Created by lang liu on 24-4-23.
//#ifndef OJ_UTIL_HPP
#define OJ_UTIL_HPP#include "log.hpp"
#include <sys/time.h>
#include <sys/stat.h>
#include <fstream>
#include <atomic>
#include <unordered_map>
#include <filesystem>
#include <vector>namespace ns_util
{using namespace ns_log;// 定义文件后缀名的映射表static inline std::unordered_map<std::string, std::string> suffixTable {{"c_cpp", ".cc"},{"csharp", ".cs"},{"python", ".py"},{"javascript", ".js"}};// 定义可执行文件后缀名的映射表static inline std::unordered_map<std::string, std::string> excuteTable {{"c_cpp", ".exe"},{"csharp", ".cs"},{"javascript", ".js"},{"python", ".py"}};// 时间工具类class TimeUtil{public:// 获取时间戳(秒)static std::string GetTimeStamp(){struct timeval _tv{};gettimeofday(&_tv, nullptr);    // 获取时间戳return std::to_string(_tv.tv_sec);}// 获取时间戳(毫秒),用于生成随机文件名static std::string GetTimeNs(){struct timeval _tv{};gettimeofday(&_tv, nullptr);return std::to_string(_tv.tv_sec * 1000 + _tv.tv_usec / 1000);}};// 文件路径和后缀名变量static std::string path;static std::string srcSuffix;static std::string excuteSuffix;static const std::string temp_path = "./template/";// 路径工具类class PathUtil{public:// 初始化模板路径和后缀名static void InitTemplate(const std::string& lang) {path = temp_path + lang + "/";srcSuffix = suffixTable.at(lang);excuteSuffix = excuteTable.at(lang);}// 添加后缀名到文件名static std::string AddSuffix(const std::string &file_name, const std::string& suffix){std::string path_name = path;path_name += file_name;path_name += suffix;return path_name;}// 获取源文件路径static std::string Src(const std::string& file_name){return AddSuffix(file_name, srcSuffix);}// 获取可执行文件路径static std::string Exe(const std::string& file_name){return AddSuffix(file_name, excuteSuffix);}// 获取编译错误文件路径static std::string CompilerError(const std::string& file_name){return AddSuffix(file_name, ".compile_error");}// 获取标准输入文件路径static std::string Stdin(const std::string& file_name){return AddSuffix(file_name, ".stdin");}// 获取标准输出文件路径static std::string Stdout(const std::string& file_name){return AddSuffix(file_name, ".stdout");}// 获取标准错误文件路径static std::string Stderr(const std::string& file_name){return AddSuffix(file_name, ".stderr");}};// 文件工具类class FileUtil{public:// 移除文件夹中的所有文件(递归)static void RemoveAllFile(const std::string& dir) {std::vector<std::filesystem::path> removeArray;try {for(auto& it : std::filesystem::directory_iterator(dir)) {if(std::filesystem::is_regular_file(it.path()))removeArray.push_back(it.path());else if (std::filesystem::is_directory(it.path()))RemoveAllFile(it.path().string());}for(auto& it : removeArray) {std::filesystem::remove(it);}}catch (const std::filesystem::filesystem_error& e) {LOG_ERROR("移除全部文件失败: {}", e.what());}}// 移除指定文件static void RemoveFile(const std::string& filename) {if(std::filesystem::exists(filename)) {std::filesystem::remove(filename);}}// 检查文件是否存在static bool IsFileExists(const std::string & path_name){struct stat st = {};if(stat(path_name.c_str(), &st) == 0)return true;return false;}// 生成全局唯一的文件名static std::string UniqFileName(){static std::atomic_uint id(0);++id;std::string ms = TimeUtil::GetTimeNs();std::string uniq_id = std::to_string(id);return ms + uniq_id;}// 将内容写入文件static bool WriteFile(const std::string & path_name, const std::string & content){std::ofstream out(path_name);if(!out.is_open()){LOG_ERROR("write file failed!");return false;}out.write(content.c_str(), (int)content.size());out.close();return true;}// 读取文件内容static bool ReadFile(const std::string& path_name, std::string* content){content->clear();std::ifstream in(path_name);if(!in.is_open()){LOG_ERROR("read file({}) failed!", path_name.c_str());return false;}in.seekg(0, std::ios::end);auto size = in.tellg(); // 获取文件大小in.seekg(0, std::ios::beg);content->resize(size);in.read(content->data(), size);return true;}};};#endif //OJ_UTIL_HPP

编译模块

编译模块因为其大部分代码都是相似的,所以这里将其具体执行逻辑分离开来,并使用工厂模式决定实例化不同的类。

编译模块

#pragma once#include <unistd.h>
#include <sys/fcntl.h>#include "log.hpp"
#include "util.hpp"namespace ns_compiler {using namespace ns_log;using namespace ns_util;// 编译器基类class Compiler {public:virtual ~Compiler() = default;// 编译函数,接受文件名作为参数int Compile(const std::string& file_name) {// 打开标准错误和标准输出文件int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);int _stdout = open(PathUtil::Stdout(file_name).c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);// 检查文件是否成功打开if(_stderr < 0 || _stdout < 0) {LOG_ERROR("打开文件失败");return -1;}// 创建子进程进行编译pid_t pid = fork();if(pid < 0 ) {LOG_ERROR("线程创建失败: {}", strerror(errno));return -1;}else if(pid == 0) {// 子进程umask(0); // 重置文件掩码// 重定向标准错误和标准输出到文件dup2(_stderr, STDERR_FILENO);dup2(_stdout, STDOUT_FILENO);// 执行编译命令Execute(file_name);LOG_INFO("exec错误:{}", strerror(errno));return -1; // exec 出错}// 父进程等待子进程结束waitpid(pid, nullptr, 0);// 检查编译后的可执行文件是否存在if(!FileUtil::IsFileExists(PathUtil::Exe(file_name))) {LOG_INFO("文件编译失败");return -2;}return 0; // 编译成功}protected:// 执行具体编译命令的纯虚函数,需由子类实现virtual void Execute(const std::string& filename) = 0;};// C++ 编译器类,继承自 Compilerclass CppCompiler : public Compiler {protected:// 执行 g++ 编译命令void Execute(const std::string& filename) override {execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(),PathUtil::Src(filename).c_str(), nullptr);}};// 编译器工厂类,用于创建编译器对象class CompilerFactory {public:// 根据语言创建相应的编译器对象static std::unique_ptr<Compiler> CreateCompiler(const std::string& lang) {if(lang == "c_cpp")return std::make_unique<CppCompiler>();// 如果需要支持其他语言,可以在这里添加相应的编译器创建逻辑elsereturn {};}};
}

运行模块

运行模块与编译模块的逻辑其实相差不大。

#pragma once#include "compiler.hpp"
#include <iostream>
#include <sys/resource.h>
#include <sys/wait.h>using namespace ns_log;
using namespace ns_util;// Runner 基类,负责运行已编译的程序
class Runner {
public:// 运行指定的程序文件,并限制其 CPU 和内存使用int Run(const std::string& filename, const int cpu_limit, const int mem_limit) {// 获取输入、输出和错误文件路径std::string _execute = PathUtil::Exe(filename);std::string _stdin = PathUtil::Stdin(filename);std::string _stdout = PathUtil::Stdout(filename);std::string _stderr = PathUtil::Stderr(filename);umask(0); // 重置文件掩码// 打开标准输入、输出和错误文件int stdin_fd = open(_stdin.c_str(), O_CREAT | O_WRONLY, 0644);int stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);int stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);// 创建子进程以执行程序pid_t pid = fork();if(pid < 0) {LOG_ERROR("fork error");close(stdin_fd);close(stdout_fd);close(stderr_fd);return -1;}else if(pid == 0) {// 子进程dup2(stdin_fd, STDIN_FILENO); // 重定向标准输入到文件dup2(stdout_fd, STDOUT_FILENO); // 重定向标准输出到文件dup2(stderr_fd, STDERR_FILENO); // 重定向标准错误到文件signal(SIGXCPU, [](int sig) { // 处理超时信号std::cerr << "timeout" << std::endl;});SetProcLimit(mem_limit, cpu_limit); // 设置进程资源限制Excute(_execute); // 执行具体的程序LOG_ERROR("execute error");exit(-1); // exec 出错}// 父进程等待子进程完成int status;waitpid(pid, &status, 0);close(stderr_fd);close(stdout_fd);close(stdin_fd);return WEXITSTATUS(status); // 返回子进程的退出状态}virtual ~Runner() = default;protected:// 纯虚函数,由子类实现具体的执行逻辑virtual void Excute(const std::string& filename) = 0;// 设置进程的内存和 CPU 使用限制static void SetProcLimit(const int m_limit, const int c_limit) {rlimit memLimit{};memLimit.rlim_cur = m_limit * 1024; // 内存限制(KB 转换为字节)memLimit.rlim_max = RLIM_INFINITY; // 最大内存限制setrlimit(RLIMIT_AS, &memLimit);rlimit cpuLimit{};cpuLimit.rlim_cur = c_limit; // CPU 时间限制(秒)cpuLimit.rlim_max = RLIM_INFINITY; // 最大 CPU 时间限制setrlimit(RLIMIT_CPU, &cpuLimit);}
};// C++ 运行器类,继承自 Runner
class CppRunner : public Runner {
protected:// 使用 execlp 执行编译后的 C++ 程序void Excute(const std::string& exec) override {execlp(exec.c_str(), exec.c_str(), nullptr);}
};// C# 运行器类,继承自 Runner
class CsharpRunner : public Runner {
protected:// 使用 execlp 执行 C# 程序void Excute(const std::string &filename) override {execlp("dotnet", "dotnet", "run",  filename.c_str(), "--project", path.c_str(), nullptr);}
};// JavaScript 运行器类,继承自 Runner
class JsRunner : public Runner {
protected:// 使用 execlp 执行 JavaScript 程序void Excute(const std::string &filename) override {execlp("node", "node", filename.c_str(), nullptr);}
};// Python 运行器类,继承自 Runner
class PyRunner : public Runner {
protected:// 使用 execlp 执行 Python 程序void Excute(const std::string &filename) override {execlp("python3", "python3", filename.c_str(), nullptr);}
};// 运行器工厂类,用于创建运行器对象
class RunnerFactory {
public:// 根据语言创建相应的运行器对象static std::unique_ptr<Runner> CreateRunner(const std::string& language) {if(language == "c_cpp")return std::make_unique<CppRunner>();else if (language == "csharp")return std::make_unique<CsharpRunner>();else if(language == "python")return std::make_unique<PyRunner>();elsereturn std::make_unique<JsRunner>();}
};

编译运行模块

编译运行模块是编译模块与运行模块的整合,用于解析前端发送的信息,并编译运行,最后将结果返回给前端网页。

#pragma once#include "runner.hpp"
#include "compiler.hpp"
#include "util.hpp"
#include <json/json.h>using namespace ns_log;
using namespace ns_util;
using namespace ns_compiler;// json 输入格式
// code: 代码
// language: 编译器
// cpu_limit: CPU 限制
// mem_limit: 内存限制// json 输出格式
// reason: 错误原因
// status: 状态码
// stderr: 错误输出
// stdout: 标准输出class CompileAndRun
{
private:// 将状态码转换为描述信息static std::string codeToDesc(int status) {static std::unordered_map<int, std::string> errTable = {{-1, "未知错误"},{-2, "编译错误"},{0, "运行成功"},// 更多状态码描述};return errTable[status];}public:// 编译并运行static void Start(std::string& in_json, std::string* out_json){// 转换 JSON 格式Json::Value in_value;Json::Value out_value;Json::Reader reader;if (!reader.parse(in_json, in_value)){LOG_ERROR("CompileAndRun::Start parse json error");return;}// 记录输入的 JSON 数据Json::StyledWriter styledWriter;LOG_DEBUG("in_value: {}", styledWriter.write(in_value));// 从 JSON 中提取参数std::string code = in_value["code"].asString();int cpu_limit = in_value["cpu_limit"].asInt();int mem_limit = in_value["mem_limit"].asInt();std::string lang = in_value["language"].asString();int status_code = 0;// 初始化模板路径和后缀名PathUtil::InitTemplate(lang);std::string file_name = FileUtil::UniqFileName(); // 生成唯一文件名// 创建编译器和运行器对象auto Cp = CompilerFactory::CreateCompiler(lang);auto Rn = RunnerFactory::CreateRunner(lang);if(code.empty()){// 如果代码为空,记录错误并设置状态码LOG_ERROR("CompileAndRun::Start code is empty");status_code = -1;goto END;}// 将代码写入文件if(!FileUtil::WriteFile(PathUtil::Src(file_name), code)){status_code = -1; // 其他错误goto END;}// 编译代码if(Cp){if(Cp->Compile(file_name) < 0) {status_code = -2; // 编译错误goto END;}}// 运行编译后的程序status_code = Rn->Run(file_name, cpu_limit, mem_limit);if(status_code < 0){status_code = -1; // 未知错误goto END;}else if(status_code > 0){goto END;}END:// 构建输出 JSON 数据out_value["status"] = status_code;out_value["reason"] = codeToDesc(status_code);if(status_code == 0){std::string _stdout;FileUtil::ReadFile(PathUtil::Stdout(file_name), &_stdout); // 读取标准输出out_value["stdout"] = _stdout;}else{std::string _stderr;FileUtil::ReadFile(PathUtil::Stderr(file_name), &_stderr); // 读取错误输出out_value["stderr"] = _stderr;}Json::StyledWriter writer;*out_json = writer.write(out_value); // 将结果写入输出 JSONLOG_DEBUG("out_json: {}", *out_json);// 清理生成的文件RemoveFile(file_name);}private:// 移除生成的文件static void RemoveFile(const std::string& filename) {auto filepath = path + filename;auto filesrc = filepath + srcSuffix;auto fileexe = filepath + excuteSuffix;auto fileout = PathUtil::Stderr(filename);auto filein = PathUtil::Stdin(filename);auto fileerr = PathUtil::Stdout(filename);FileUtil::RemoveFile(filesrc);FileUtil::RemoveFile(fileexe);FileUtil::RemoveFile(fileout);FileUtil::RemoveFile(filein);FileUtil::RemoveFile(fileerr);}
};

程序入口

#include "compile_run.hpp"
#include <httplib.h>int main()
{using namespace httplib;using namespace ns_util;// system("pwd");Server svr;svr.set_base_dir("./wwwroot");svr.Post("/run", [](const Request & req, Response &res) {std::string in_json = req.body;std::string out_json;CompileAndRun::Start(in_json, &out_json);res.set_content(out_json, "application/json");});// 启动服务器svr.listen("0.0.0.0", 8080);return 0;
}

总结

这个简易在线编译器只是我用于学习多进程应用与前后端交互而写的超微小项目,不算上前端网页只有500行代码左右。虽然还有很多设计不完善的地方,但凭我现在的水平也暂时想不到更好的解决方案,有什么不足的地方,欢迎讨论。

github:完整代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/44020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+antdv仿百度网盘样式文件夹管理组件

实现&#xff1a; 默认进入页面时&#xff0c;文件夹全选&#xff1b;文件夹状态&#xff0c;以及文件夹内的文件选择状态&#xff0c;与组件联动文件夹数量&#xff0c;根据后端数据动态生成 实现思路&#xff1a; 将后端数据存到vuex中&#xff0c;增加&#xff08;多选框…

LAMP万字详解(概念、构建步骤)

目录 LAMP Apache 起源 主要特点 软件版本 编译安装httpd服务器 编译安装的优点 操作步骤 准备工作 编译 安装 优化执行路径 添加服务 守护进程 配置httpd 查看 Web 站点的访问情况 虚拟主机 类型 部署基于域名的虚拟主机 为虚拟主机提供域名解析&#xff…

机器人三定律及伦理分析

全世界的机器人定律并没有一个统一的标准或体系&#xff0c;但是在科学文献中&#xff0c;最广为人知的是由科幻小说家阿西莫夫提出的“机器人三定律”。本文将以这些定律为基础&#xff0c;分析现有的机器人伦理和实际应用中的问题&#xff0c;给出若干实例&#xff0c;并对相…

EPSON LQ80KF II驱动 打印机 0x00000003e3

1.添加打印机 2.按名次选择共享打印机,输入共享打印机ip 3.选择创建新端口 4.选择打印机驱动

[安洵杯 2019]easy_serialize_php

源码&#xff1a; <?php$function $_GET[f];function filter($img){$filter_arr array(php,flag,php5,php4,fl1g);$filter /.implode(|,$filter_arr)./i;return preg_replace($filter,,$img); }if($_SESSION){unset($_SESSION); }$_SESSION["user"] guest; …

申请商标用什么颜色:企业和个人申请注册商标攻略!

在申请注册商标到底要用什么颜色&#xff0c;许多初次申请注册主体都不是特别清楚&#xff0c;普推知产商标老杨建议&#xff0c;在一般情况下建议尽量用黑白色&#xff0c;因为商标用黑白色在使用时可以着任何色。 在用黑色申请注册成功&#xff0c;别的主体用其它颜色要在同…

【python】PyQt5事件传递,鼠标动作捕获,键盘按键捕获原理与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

部署前端项目

常见部署方式有&#xff1a;静态托管服务、服务器部署 1. 静态托管服务 使用平台部署代码&#xff0c;比如 GitHub。 | 创建一个仓库&#xff0c;仓库名一般是 yourGithubName.github.io。 | 将打包后的静态文件文件上传到仓库。 | 在“Settings”&#xff08;选项&#xff0…

C语言编程4:复合赋值,递增递减运算符,局部变量与全局变量,本地变量,转义字符

一篇文章带你玩转C语言基础语法4&#xff1a;复合赋值&#xff0c;递增递减运算符&#xff0c;局部变量与全局变量&#xff0c;本地变量&#xff0c;转义字符 一、复合赋值&#x1f33f; 1.1&#x1f4a0;定义 赋值就是给任意一个变量或者常量赋一个值&#xff0c;这个值可以…

javaweb零碎知识3

// 假设您已经导入了 axios import axios from axios;// 获取表单元素 const form document.getElementById(myForm);// 为表单添加 submit 事件监听器 form.addEventListener(submit, function(e) {// 阻止表单的默认提交行为e.preventDefault();// 创建 FormData 对象并从表…

福建 | 南安帝兴混凝土电子签收的困难和突破

01 发展从来都是从困难开始 混凝土发货单实现无纸化签收&#xff0c;众多业内人士认为这个概念很好&#xff0c;但能否落地却大多抱有怀疑态度&#xff0c;理由多种多样&#xff1a; “行业太传统&#xff0c;接受不了新鲜事物。” “驾驶员年龄偏大&#xff0c;玩不来智能手…

【分布式系统管理框架】Zookeeper集群

1、Zookeeper &#xff08;1&#xff09;Zookeeper定义 Zookeeper是一个开源的&#xff0c;为分布式框架提供协调服务的Apache项目。 &#xff08;2&#xff09;Zookeeper工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架…

怎么制作gif动图,视频制作GIF动画更简单

在社交媒体和网络交流中&#xff0c;GIF动画以其生动活泼的表现形式成为了表达情感、幽默和创意的热门媒介。无论是分享日常趣事&#xff0c;还是制作专业演示&#xff0c;一个恰到好处的GIF动图总能吸引目光&#xff0c;传递信息。 但你知道吗&#xff1f;即使没有专业的设计背…

工业一体机为数字化工厂带来高效作业指导

随着工业4.0的浪潮席卷全球&#xff0c;数字化工厂的概念深入人心。在这一背景下&#xff0c;工业一体机作为数字化转型的重要一环&#xff0c;凭借其强大的功能和灵活的应用&#xff0c;为工厂实现高效作业指导提供了强大的助力。 一、工业一体机的优势&#xff1a;赋能数字化…

2024年全国青少年信息素养大赛图形化编程小高组复赛真题

2024年全国青少年信息素养大赛图形化编程小高组复赛真题 题目总数&#xff1a;6 总分数&#xff1a;100 编程题 第 1 题 问答题 请对变身鱼进行编程&#xff0c;变身鱼的初始状态已经设置&#xff0c;不需要进行修改&#xff0c; 1.当变身鱼大小大于300时&#xff0…

数据高效交互丨DolphinDB Redis 插件使用指南

DolphinDB 是一个高性能的分布式数据库。通过 Redis 插件&#xff0c;DolphinDB 用户可以轻松地与 Redis 数据库进行交互。用户不仅可以从 DolphinDB 向 Redis 发送数据&#xff0c;实现高速的数据写入操作&#xff1b;还可以从 Redis 读取数据&#xff0c;将实时数据流集成到 …

PaddleVideo:Squeeze Time算法移植

参考PaddleVideo/docs/zh-CN/contribute/add_new_algorithm.md at develop PaddlePaddle/PaddleVideo GitHubAwesome video understanding toolkits based on PaddlePaddle. It supports video data annotation tools, lightweight RGB and skeleton based action recognitio…

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》 参考文档&#xff1a;ESP-DL 用户指南&#xff1a; https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/index.html 和https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/get-s…

PHP禁止IP访问和IP段访问(代码实例)

PHP禁止IP和IP段访问 实现IP限制是Web开发中常见的需求之一&#xff0c;它可以用于限制特定IP地址的访问权限。在PHP中&#xff0c;我们可以通过一些方法来实现IP限制。 <?//禁止某个IP$banned_ip array ("127.0.0.1",//"119.6.20.66","192.168.…

C#中简单Socket编程

C#中简单Socket编程 Socket分为面向连接的套接字(TCP套接字)和面向消息的套接字(UDP 套接字)。我们平时的网络编程是对Socket进行操作。 接下来&#xff0c;我用C#语言来进行简单的TCP通信和UDP通信。 一、TCP通信 新建项目SocketTest&#xff0c;首先添加TCP通信的客户端代…