【Linux网络】构建类似XShell功能的TCP服务器
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨
文章目录
- 🏳️🌈一、TcpServer.hpp
- 1.1 基本结构
- 1.2 多线程处理函数
- 🏳️🌈二、Common.hpp
- 2.1 基本结构
- 2.2 构造、析构函数
- 2.3 SafeCheck()
- 2.4 Excute()
- 2.5 HandlerCommand()
- 🏳️🌈三、TcpServer.cpp
- 🏳️🌈四、测试
- 🏳️🌈五、整体代码
- 5.1 TcpServer.hpp
- 5.2 TcpServer.cpp
- 5.3 Command.hpp
- 👥总结
在上一篇文章中,笔者带大家实现了TCP服务器的 4种 客户端与服务端通信的模式 ,分别是单执行流模式、多进程模式、多线程模式、以及线程池模式
这一篇,我将带大家进一步理解TCP,就从用TCP实现类XShell功能服务器 ,采用的是多线程版本
哦
🏳️🌈一、TcpServer.hpp
其他部分保持不变,我们添加一个处理回调函数
,用来判断和执行相应的 xshell
命令
1.1 基本结构
using handler_t = std::function<std::string(int sockfd, InetAddr addr)>;class TcpServer{public:// 构造函数TcpServer(handler_t handler,uint16_t port = gport):_handler(handler), _port(port), _isrunning(false){}// 初始化void InitServer(){}// server - 2 多线程版本void Loop(){}// 析构函数~TcpServer(){}private:int _listensockfd; // 监听socketuint16_t _port;bool _isrunning;handler_t _handler;
};
1.2 多线程处理函数
我们之前使用 Server
进行回显处理,这里将 Execute 函数的处理方法从 Server 改成 _handler就行了
// 线程函数static void* Execute(void* args){ThreadDate* td = static_cast<ThreadDate*>(args);// 子线程结束后由系统自动回收资源,无需主线程调用 pthread_joinpthread_detach(pthread_self()); // 分离新线程,无需主线程回收td->_self->_handler(td->_sockfd, td->_addr);delete td;return nullptr;}
🏳️🌈二、Common.hpp
2.1 基本结构
Command
类实现类似于XShell的功能,但是需要注意要让所有命令都可以执行,因为可能会导致删库等相关的问题。不成员变量可以使用set容器存储允许执行命令的前缀!
class Command{public:Command(){}bool SafeCheck(const std::string& cmdstr){}std::string Excute(const std::string& cmdstr){}void HandlerCommand(int sockfd, InetAddr addr){}~Command(){}private:std::set<std::string> _safe_command; // 允许执行的命令
};
2.2 构造、析构函数
根据自己的需要,在构造函数种将允许使用的命令插入到容器,析构函数无需处理!
Command() {// 白名单_safe_command.insert("ls");_safe_command.insert("pwd");_safe_command.insert("touch");_safe_command.insert("whoami");_safe_command.insert("which");
}
~Command(){}
2.3 SafeCheck()
检查当前命令是否在白名单中
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;}
2.4 Excute()
写这部分代码时,我们需要用到一个函数 - popen
popen 的作用是 创建子进程执行系统命令,并通过管道(Pipe)与其通信,他会启动子进程,调用 /bin/sh(或其他默认 shell)解析并执行指定的命令(如 ls -l、grep “error” 等)。
std::string Excute(const std::string& cmdstr) {// 检查是否安全,不安全返回if (!SafeCheck(cmdstr)) {return "unsafe";}std::string result;// popen 创建子进程执行系统命令,并通过管道(Pipe)与其通信// popen(const char* command, const char* type)// command: 命令字符串// type: 管道类型,"r"表示读,"w"表示写,"r+"表示读写// 返回文件指针,失败返回NULLFILE* fp = popen(cmdstr.c_str(), "r");if (fp) {// 读取子进程的输出// 一行读取char line[1024];while (fgets(line, sizeof(line), fp)) {result += line;}return result.empty() ? "success" : result; // 有些命令创建无返回值}
}
2.5 HandlerCommand()
命令处理函数是一个长服务(死循环),先接收客户端的信息,如果接收成功则处理收到的消息(命令),并将处理的结果发送给客户端,如果读到文件结尾或者接收失败则退出循环!
void HandlerCommand(int sockfd, InetAddr addr) {// 我们把它当作一个长服务while (true) {char commandbuffer[1024]; // 接收命令的缓冲区// 1. 接收消息// recv(int sockfd, void* buf, size_t len, int flags)// sockfd: 套接字描述符// buf: 接收缓冲区// len: 接收缓冲区大小// flags: 接收标志 0表示阻塞,非0表示非阻塞ssize_t n = ::recv(sockfd, commandbuffer, sizeof(commandbuffer) - 1, 0);if (n > 0) {commandbuffer[n] = 0;LOG(LogLevel::INFO) << "get command from client" << addr.AddrStr()<< ":" << commandbuffer;std::string result = Excute(commandbuffer);// 2. 发送消息// send(int sockfd, const void* buf, size_t len, int flags)// sockfd: 套接字描述符// buf: 发送缓冲区// len: 发送缓冲区大小// flags: 发送标志 0表示不阻塞,非0表示阻塞::send(sockfd, result.c_str(), result.size(), 0);}// 读到文件结尾else if (n == 0) {LOG(LogLevel::INFO) << "client " << addr.AddrStr() << " quit";break;} else {LOG(LogLevel::ERROR) << "read error from client " << addr.AddrStr();break;}}
}
🏳️🌈三、TcpServer.cpp
服务端主函数使用智能指针构造Server对象(参数需要加执行方法) ,然后调用初始化与执行函数,调用主函数使用该可执行程序 + 端口号!
需要注意的是,Command::HandlerCommand 是 非静态成员函数,调用时必须通过 Command 类的实例(如 cmdservice)来访问。
#include "TcpServer.hpp"
#include "Command.hpp"int main(int argc, char* argv[]){if(argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;Die(1);}uint16_t port = std::stoi(argv[1]);Command cmdservice;std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(// &Command::HandlerCommand:成员函数指针。// &cmdservice:Command 对象的实例指针(this 指针)// _1 和 _2:占位符,表示回调函数接受两个参数(int sockfd 和 InetAddr addr)std::bind(&Command::HandlerCommand, &cmdservice, std::placeholders::_1, std::placeholders::_2),port);tsvr->InitServer();tsvr->Loop();return 0;
}
🏳️🌈四、测试
🏳️🌈五、整体代码
5.1 TcpServer.hpp
#pragma once#include <iostream>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <functional>#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"#define BACKLOG 8using namespace LogModule;
using namespace ThreadPoolModule;static const uint16_t gport = 8080;using handler_t = std::function<void(int sockfd, InetAddr addr)>;class TcpServer{public:// 构造函数TcpServer(handler_t handler,uint16_t port = gport):_handler(handler), _port(port), _isrunning(false){}// 初始化void InitServer(){// 1. 创建 socket_listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);if(_listensockfd < 0){LOG(LogLevel::ERROR) << "create socket error: " << strerror(errno);Die(2);}LOG(LogLevel::INFO) << "create sockfd success: " << _listensockfd;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. 绑定 socketif(::bind(_listensockfd, CONV(&local), sizeof(local)) < 0){LOG(LogLevel::ERROR) << "bind socket error: " << strerror(errno);Die(3);}LOG(LogLevel::INFO) << "bind sockfd success: " << _listensockfd;// 3. 因为 tcp 是面向连接的,tcp需要未来不断地获取连接// listen 就是监听连接的意思,所以需要设置一个队列,来保存等待连接的客户端// 队列的长度为 8,表示最多可以有 8 个客户端等待连接// listen(int sockfd, int backlog)// sockfd 就是之前创建的 socket 句柄// backlog 就是队列的长度// 返回值:成功返回 0,失败返回 -1if(::listen(_listensockfd, BACKLOG) < 0){LOG(LogLevel::ERROR) << "listen socket error: " << strerror(errno);Die(4);}LOG(LogLevel::INFO) << "listen sockfd success: " << _listensockfd;}// server - 2 多线程版本// 为每个新连接分配独立的线程处理业务逻辑// (Loop 函数)通过 accept 循环监听新连接,为每个新连接创建线程(pthread_create)传递连接信息给子线程(通过 ThreadDate 结构体)// (Execute 函数)调用 pthread_detach 分离自身(避免主线程调用 pthread_join)执行 Server 函数处理具体业务逻辑线程结束后自动释放资源(通过 delete td)// (Server 函数)循环读取客户端数据(read),处理业务逻辑(示例中的回显服务),发送响应(write)关闭连接(close(sockfd))void Loop(){_isrunning = true;while(_isrunning){struct sockaddr_in client;socklen_t len = sizeof(client);// 1. 获取新连接int sockfd = ::accept(_listensockfd, CONV(&client), &len);if(sockfd < 0){LOG(LogLevel::ERROR) << "accept socket error: " << strerror(errno);continue;}InetAddr cli(client);LOG(LogLevel::INFO) << "accept new connection from " << cli.AddrStr() << " sockfd: " << sockfd;// 获取成功pthread_t tid;ThreadDate* td = new ThreadDate(sockfd, this, cli);// pthread_create 第一个参数是线程id,第二个参数是线程属性,第三个参数是线程函数,第四个参数是线程函数参数pthread_create(&tid, nullptr, Execute, td);}_isrunning = false;}// 线程函数参数对象class ThreadDate{public:int _sockfd;TcpServer* _self;InetAddr _addr;public:ThreadDate(int sockfd, TcpServer* self, const InetAddr& addr): _sockfd(sockfd), _self(self), _addr(addr){}};// 线程函数static void* Execute(void* args){ThreadDate* td = static_cast<ThreadDate*>(args);// 子线程结束后由系统自动回收资源,无需主线程调用 pthread_joinpthread_detach(pthread_self()); // 分离新线程,无需主线程回收td->_self->_handler(td->_sockfd, td->_addr);delete td;return nullptr;}// 析构函数~TcpServer(){}private:int _listensockfd; // 监听socketuint16_t _port;bool _isrunning;handler_t _handler;
};
5.2 TcpServer.cpp
#include "TcpServer.hpp"
#include "Command.hpp"int main(int argc, char* argv[]){if(argc != 2){std::cerr << "Usage: " << argv[0] << " port" << std::endl;Die(1);}uint16_t port = std::stoi(argv[1]);Command cmdservice;std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(// &Command::HandlerCommand:成员函数指针。// &cmdservice:Command 对象的实例指针(this 指针)// _1 和 _2:占位符,表示回调函数接受两个参数(int sockfd 和 InetAddr addr)std::bind(&Command::HandlerCommand, &cmdservice, std::placeholders::_1, std::placeholders::_2),port);tsvr->InitServer();tsvr->Loop();return 0;
}
5.3 Command.hpp
#pragma once#include <iostream>
#include <set>
#include <cstring>
#include <cstdio>#include "InetAddr.hpp"
#include "Log.hpp"using namespace LogModule;class Command{public:Command(){// 白名单_safe_command.insert("ls");_safe_command.insert("pwd");_safe_command.insert("touch");_safe_command.insert("whoami");_safe_command.insert("which");}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";}std::string Excute(std::string& cmdstr){// 检查是否安全,不安全返回if(!SafeCheck(cmdstr)){return "unsafe";}std::string result;// popen 创建子进程执行系统命令,并通过管道(Pipe)与其通信// popen(const char* command, const char* type)// command: 命令字符串// type: 管道类型,"r"表示读,"w"表示写,"r+"表示读写// 返回文件指针,失败返回NULLFILE* fp = popen(cmdstr.c_str(), "r");if(fp){// 读取子进程的输出// 一行读取char line[1024];while(fgets(line, sizeof(line), fp)){result += line;}return result.empty() ? "success" : result; // 有些命令创建无返回值}}void HandlerCommand(int sockfd, InetAddr addr){// 我们把它当作一个长服务while(true){char commandbuffer[1024]; // 接收命令的缓冲区// 1. 接收消息// recv(int sockfd, void* buf, size_t len, int flags)// sockfd: 套接字描述符// buf: 接收缓冲区// len: 接收缓冲区大小// flags: 接收标志 0表示阻塞,非0表示非阻塞ssize_t n = ::recv(sockfd, commandbuffer, sizeof(commandbuffer) - 1, 0); if(n > 0){commandbuffer[n] = 0;LOG(LogLevel::INFO) << "get command from client" << addr.AddrStr() << ":" << commandbuffer;std::string result = Excute(commandbuffer);// 2. 发送消息// send(int sockfd, const void* buf, size_t len, int flags)// sockfd: 套接字描述符// buf: 发送缓冲区// len: 发送缓冲区大小// flags: 发送标志 0表示不阻塞,非0表示阻塞::send(sockfd, result.c_str(), result.size(), 0);}// 读到文件结尾else if(n == 0){LOG(LogLevel::INFO) << "client " << addr.AddrStr() << " quit";break;}else{LOG(LogLevel::ERROR) << "read error from client " << addr.AddrStr();break;}}}~Command(){}private:std::set<std::string> _safe_command; // 允许执行的命令
};
👥总结
本篇博文对 【Linux网络】构建类似XShell功能的TCP服务器 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~
相关文章:

【Linux网络】构建类似XShell功能的TCP服务器
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...

Spring Boot 配置源详解(完整版)
Spring Boot 配置源详解(完整版) 一、配置源加载顺序与优先级 配置源类型优先级顺序(从高到低)对应配置类/接口是否可覆盖典型文件/来源命令行参数(--keyvalue)1(最高)SimpleComman…...
JDK 17 与 Spring Cloud Gateway 新特性实践指南
一、环境要求与版本选择 1. JDK 17 的必要性 最低版本要求:Spring Boot 3.x 及更高版本(如 3.4)强制要求 JDK 17,以支持 Java 新特性(如密封类、模式匹配)和性能优化。JDK 17 核心特性: 密封类…...
异构迁移学习(无创脑机接口中的跨脑电帽迁移学习)
本文介绍BCI中的跨脑电帽的迁移学习最新算法。 (发表于2025 arxiv,应该属于投稿阶段,这个场景具有非常不错的研究意义和前景) 最新跨脑电帽异构算法github开源代码 SDDA算法原文 一、脑机接口绪论 脑机接口(BCI)指在人或动物大脑与外部设备之间创建的直接连接,通过脑…...

puppeteer注入浏览器指纹过CDP
一、背景 通过puppeteer爬取目标网站时,经常会被对方网站检测到,比如原生puppeteerCDP特征非常明显,另外指纹如果一直不变,也会引发风控 二、实现 通过以下几行代码即可轻松过大部分检测点,并且能够切换指纹&#x…...
1.8软考系统架构设计师:系统架构设计师概述 - 练习题附答案及超详细解析
系统架构设计师概述综合知识单选题 每道题均附有答案解析: 架构设计师的定义、职责和任务 1、系统架构设计师的核心职责是: A. 编写具体功能模块的代码 B. 制定系统整体架构和技术选型 C. 管理项目预算和进度 D. 直接对接客户进行销售支持 答案&#x…...

软件项目实施全流程及交付物清单
需求分析 -> 概要设计 -> 详细设计 -> 开发实现 -> 测试 -> 部署 -> 运维 一、确认项目目标、范围和团队成员 二、收集和分析客户需求,确定需求规格 三、制定详细的项目计划,包括时间表、资源计划、预算 四、系统架构设计…...

【2025计算机网络-面试常问】http和https区别是什么,http的内容有哪些,https用的是对称加密还是非对称加密,流程是怎么样的
HTTP与HTTPS全面对比及HTTPS加密流程详解 一、HTTP与HTTPS核心区别 特性HTTPHTTPS协议基础明文传输HTTP SSL/TLS加密层默认端口80443加密方式无加密混合加密(非对称对称)证书要求不需要需要CA颁发的数字证书安全性易被窃听、篡改、冒充防窃听、防篡改…...

从梯度消失到百层网络:ResNet 是如何改变深度学习成为经典的?
自AlexNet赢得2012年ImageNet竞赛以来,每个新的获胜架构通常都会增加更多层数以降低错误率。一段时间内,增加层数确实有效,但随着网络深度的增加,深度学习中一个常见的问题——梯度消失或梯度爆炸开始出现。 梯度消失问题会导致梯…...
2025.4.26总结
今天把马良老师的《职场十二法则》看完后,感触极大,这们课程就是一场职场启蒙课。 虽然看过不少关于职场的书籍,但大多数是关于职场进阶,方法方面的。并没有解答“面对未来二三十年的职场生涯,我该怎么去看待自己的工…...

Uni-App 多端电子合同开源项目介绍
项目概述 本项目是一款基于 uni-app框架开发的多端电子合同管理平台,旨在为企业及个人用户提供高效、安全、便捷的电子合同签署与管理服务。项目创新性地引入了 “证据链”与“非证据链”两种签署模式,满足不同场景下的签署需求,支持多种签署…...

多语言笔记系列:共享数据
在笔记中共享数据(变量) 使用 .NET 交互式内核,可以在单个笔记本中以多种语言编写代码。为了利用每种语言的不同优势,您会发现在它们之间共享数据很有用。即一种语言的变量,可以在其它语言中使用。 默认情况下,.NET Interactive …...

如何使用SeedProd创建无缝的WordPress维护页面
不管您刚接触 WordPress ,还是经验丰富的站长,SeedProd 都是创建网站维护页面的得力助手。通过SeedProd,您可以轻松创建一个与网站风格一致、功能齐全的维护页面,让您的用户在网站维护期间也能感受到您的专业与关怀。本文将为您提…...

使用Python设置excel单元格的字体(font值)
一、前言 通过使用Python的openpyxl库,来操作excel单元格,设置单元格的字体,也就是font值。 把学习的过程分享给大家。大佬勿喷! 二、程序展示 1、新建excel import openpyxl from openpyxl.styles import Font wb openpyxl.…...
【PCB工艺】推挽电路及交越失真
推挽电路(Push-Pull Circuit) 推挽电路(Push-Pull Circuit) 是一种常用于功率放大、电机驱动、音频放大等场合的电路结构,具有输出对称、效率高、失真小等优点。 什么是推挽电路? 推挽是指:由两种极性相反的器件(如 NPN 和 PNP、NMOS 和 PMOS)交替导通,一个“推”电…...
告别手动映射:在 Spring Boot 3 中优雅集成 MapStruct
在日常的后端开发中,我们经常需要在不同的对象之间进行数据转换,例如将数据库实体(Entity)转换为数据传输对象(DTO)发送给前端,或者将接收到的 DTO 转换为实体进行业务处理或持久化。手动进行这…...
uv run 都做了什么?
uv run 都做了什么? uv run <命令> [参数...] 的主要作用是:在一个由 uv 管理或发现的 Python 虚拟环境中,执行你指定的 <命令>。它会临时配置一个子进程的环境,使其表现得如同该虚拟环境已经被激活一样。这意味着&am…...

求解,如何控制三相无刷电机?欢迎到访评论
问题:通过一个集成的TF2104芯片控制H桥上桥臂和下桥臂,如何控制?还是说得需要PWM_UH和PWM_UL分开控制?...
Java ThreadLocal与内存泄漏
当我们利用 ThreadLocal 来管理数据时,我们不可避免地会面临内存泄漏的风险。 原因在于 ThreadLocal 的工作方式。当我们在当前线程的 ThreadLocalMap 中存储一个值时,一旦这个值不再需要,释放它就变得至关重要。如果不这样做,那么…...

365打卡第R3周: RNN-心脏病预测
🍨 本文为🔗365天深度学习训练营中的学习记录博客 🍖 原作者:K同学啊 🏡 我的环境: 语言环境:Python3.10 编译器:Jupyter Lab 深度学习环境:torch2.5.1 torchvision0…...
1.1.1 用于排序规则的IComparable接口使用介绍
在C#中,IComparable 是一个核心接口,用于定义对象的自然排序规则。实现该接口的类可以指定其实例如何与其他实例比较大小,从而支持排序操作(如 Array.Sort()、List.Sort()). 1. 该接口CompareTo返回值含义:…...

【实战】基于强化学习的 Agent 训练框架全流程拆解
一、引言 在人工智能蓬勃发展的今天,强化学习(Reinforcement Learning, RL)作为让智能体(Agent)在复杂环境中自主学习并做出最优决策的核心技术,正日益受到关注。从游戏领域中击败人类顶尖选手的 AlphaGo&a…...

【音视频】⾳频处理基本概念及⾳频重采样
一、重采样 1.1 什么是重采样 所谓的重采样,就是改变⾳频的采样率、sample format、声道数等参数,使之按照我们期望的参数输出。 1.2 为什么要重采样 为什么要重采样? 当然是原有的⾳频参数不满⾜我们的需求,⽐如在FFmpeg解码⾳频的时候…...

Prompt 结构化提示工程
Prompt 结构化提示工程 目前ai开发工具都大同小异,随着deepseek的流行,ai工具的能力都差不太多,功能基本都覆盖到了。而prompt能力反而是需要更加关注的(说白了就是能不能把需求清晰的输出成文档)。因此大家可能需要加…...
设计心得——数据结构的意义
一、数据结构 在老一些的程序员中,可能都听说过,程序其实就是数据结构算法这种说法。它是由尼克劳斯维特在其著作《算法数据结构程序》中提出的,然后在一段时期内这种说法非常流行。这里不谈论其是否正确,只是通过这种提法&#…...
【Pandas】pandas DataFrame rdiv
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...

Pycharm 代理配置
Pycharm 代理配置 文章目录 Pycharm 代理配置1. 设置系统代理1.1 作用范围1.2 使用场景1.3 设置步骤 2. 设置 python 运行/调试代理2.1 作用范围2.2 使用场景2.3 设置步骤 Pycharm 工具作为一款强大的 IDE,其代理配置在实际开发中也是必不可少的,下面介绍…...
GPU 加速库(CUDA/cuDNN)
现代数字图像处理与深度学习任务对计算效率提出极高要求,GPU 加速库通过硬件并行计算能力大幅提升数据处理速度。 一、CUDA 并行计算架构深度解析 1. 架构设计与硬件协同 CPU-GPU 异构计算模型CPU 作为主机端,主要负责逻辑控制、任务调度以及数据预处…...

Spring Native:GraalVM原生镜像编译与性能优化
文章目录 引言一、Spring Native与GraalVM基础1.1 GraalVM原理与优势1.2 Spring Native架构设计 二、原生镜像编译实践2.1 构建配置与过程2.2 常见问题与解决方案 三、性能优化技巧3.1 内存占用优化3.2 启动时间优化3.3 实践案例分析 总结 引言 微服务架构的普及推动了轻量级、…...
JAVA JVM面试题
你的项目中遇到什么问题需要jvm调优,怎么调优的,堆的最小值和最大值设置为什么不设置成一样大? 在项目中,JVM调优通常源于以下典型问题及对应的调优思路,同时关于堆内存参数(-Xms/-Xmx)的设置逻…...