如何开发高效服务(C++ )
在 C++ 开发高效服务器时,常用的开发模式和设计模式能够帮助你构建高效、可扩展和可维护的服务器。以下是一些常见的模式和设计模式:
1. 并发和并行编程模型
1.1 Reactor 模式
Reactor 模式是一种事件驱动设计模式,广泛用于高性能服务器编程。它使用事件分离机制和事件处理器来管理多路 I/O 事件。典型实现包括使用 select、poll 或 epoll 等系统调用。
核心组件:
- Event Demultiplexer:如
select或epoll,用于等待事件。 - Event Handler:处理特定事件的回调函数。
- Synchronous Event De-multiplexer:同步事件分离器,负责监听 I/O 事件。
1.2 Proactor 模式
Proactor 模式是另一种事件驱动设计模式,区别于 Reactor 模式的是它使用异步 I/O 操作。I/O 操作在后台完成,完成后通知应用程序。
核心组件:
- Asynchronous Operation Processor:执行异步 I/O 操作。
- Completion Handler:异步操作完成后的回调函数。
2. 设计模式
2.1 单例模式(Singleton)
单例模式确保一个类只有一个实例,并提供一个全局访问点。服务器中的配置管理器或日志管理器通常使用单例模式。
class Singleton {
public:static Singleton& getInstance() {static Singleton instance;return instance;}private:Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};
2.2 工厂模式(Factory)
工厂模式用于创建对象,而不必指定具体类。它使得代码更加灵活和可扩展。服务器中常用于创建各种处理器或服务。
class AbstractProduct {
public:virtual void doSomething() = 0;virtual ~AbstractProduct() {}
};class ConcreteProductA : public AbstractProduct {
public:void doSomething() override {// Implementation for ConcreteProductA}
};class ConcreteProductB : public AbstractProduct {
public:void doSomething() override {// Implementation for ConcreteProductB}
};class Factory {
public:static std::unique_ptr<AbstractProduct> createProduct(char type) {if (type == 'A') return std::make_unique<ConcreteProductA>();if (type == 'B') return std::make_unique<ConcreteProductB>();return nullptr;}
};
2.3 观察者模式(Observer)
观察者模式定义对象间的一对多依赖关系,当一个对象改变状态时,所有依赖它的对象都会收到通知并自动更新。常用于事件系统和通知机制。
class Observer {
public:virtual void update() = 0;
};class Subject {std::vector<std::shared_ptr<Observer>> observers;public:void attach(const std::shared_ptr<Observer>& observer) {observers.push_back(observer);}void notify() {for (const auto& observer : observers) {observer->update();}}
};
2.4 策略模式(Strategy)
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以互换。服务器中常用于动态选择处理算法或策略。
class Strategy {
public:virtual void execute() = 0;
};class ConcreteStrategyA : public Strategy {
public:void execute() override {// Implementation of strategy A}
};class ConcreteStrategyB : public Strategy {
public:void execute() override {// Implementation of strategy B}
};class Context {std::unique_ptr<Strategy> strategy;public:void setStrategy(std::unique_ptr<Strategy> newStrategy) {strategy = std::move(newStrategy);}void executeStrategy() {if (strategy) {strategy->execute();}}
};
3. 多线程编程模型
3.1 线程池(Thread Pool)
线程池模式预先创建一组线程来处理任务,从而避免了频繁创建和销毁线程的开销。它可以提高服务器的性能和响应速度。
class ThreadPool {std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queueMutex;std::condition_variable condition;bool stop;public:ThreadPool(size_t threads) : stop(false) {for (size_t i = 0; i < threads; ++i) {workers.emplace_back([this] {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(this->queueMutex);this->condition.wait(lock, [this] {return this->stop || !this->tasks.empty();});if (this->stop && this->tasks.empty()) return;task = std::move(this->tasks.front());this->tasks.pop();}task();}});}}template<class F>void enqueue(F&& f) {{std::unique_lock<std::mutex> lock(queueMutex);tasks.emplace(std::forward<F>(f));}condition.notify_one();}~ThreadPool() {{std::unique_lock<std::mutex> lock(queueMutex);stop = true;}condition.notify_all();for (std::thread &worker : workers) {worker.join();}}
};
3.2 任务队列(Task Queue)
任务队列是一种将任务排队等待处理的机制。可以与线程池结合使用,实现任务的并行处理。
class TaskQueue {std::queue<std::function<void()>> tasks;std::mutex queueMutex;public:void pushTask(std::function<void()> task) {std::lock_guard<std::mutex> lock(queueMutex);tasks.push(std::move(task));}std::function<void()> popTask() {std::lock_guard<std::mutex> lock(queueMutex);if (tasks.empty()) return nullptr;auto task = tasks.front();tasks.pop();return task;}
};
4. 网络通信模式
4.1 多路复用(Multiplexing)
使用 select、poll 或 epoll 实现多路复用,允许单个线程处理多个网络连接。
#include <sys/epoll.h>int epoll_fd = epoll_create1(0);
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event);while (true) {struct epoll_event events[MAX_EVENTS];int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int n = 0; n < nfds; ++n) {if (events[n].data.fd == listen_fd) {int conn_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &client_len);event.data.fd = conn_fd;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event);} else {// Handle I/O for events[n].data.fd}}
}
总结
使用上述开发模式和设计模式,可以构建高效的 C++ 服务器。选择适合的模式和设计模式可以提高代码的可维护性、可扩展性和性能。在实际开发中,可以根据需求组合使用这些模式,构建出高效可靠的服务器应用。
实现一个简单的服务器
以下是一个基于上述开发模式和设计模式的高效 C++ 服务器的示例。该服务器使用了 Reactor 模式、线程池 和其他一些设计模式。
项目结构
我们将项目组织成以下几个部分:
- 主程序入口 (
main.cpp) - 服务器类 (
Server) - 客户端处理类 (
ClientHandler) - 线程池类 (
ThreadPool)
代码实现
1. 线程池类 (ThreadPool)
我们将先定义一个简单的线程池,用于处理客户端请求。
// ThreadPool.h
#ifndef THREADPOOL_H
#define THREADPOOL_H#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>class ThreadPool {
public:ThreadPool(size_t numThreads);~ThreadPool();void enqueue(std::function<void()> task);private:std::vector<std::thread> workers;std::queue<std::function<void()>> tasks;std::mutex queueMutex;std::condition_variable condition;bool stop;void workerThread();
};#endif // THREADPOOL_H// ThreadPool.cpp
#include "ThreadPool.h"ThreadPool::ThreadPool(size_t numThreads) : stop(false) {for (size_t i = 0; i < numThreads; ++i) {workers.emplace_back(&ThreadPool::workerThread, this);}
}ThreadPool::~ThreadPool() {{std::unique_lock<std::mutex> lock(queueMutex);stop = true;}condition.notify_all();for (std::thread &worker : workers) {worker.join();}
}void ThreadPool::enqueue(std::function<void()> task) {{std::unique_lock<std::mutex> lock(queueMutex);tasks.emplace(std::move(task));}condition.notify_one();
}void ThreadPool::workerThread() {while (true) {std::function<void()> task;{std::unique_lock<std::mutex> lock(queueMutex);condition.wait(lock, [this] { return stop || !tasks.empty(); });if (stop && tasks.empty()) {return;}task = std::move(tasks.front());tasks.pop();}task();}
}
2. 客户端处理类 (ClientHandler)
处理客户端的连接和请求。
// ClientHandler.h
#ifndef CLIENTHANDLER_H
#define CLIENTHANDLER_H#include <unistd.h>
#include <iostream>class ClientHandler {
public:ClientHandler(int clientSocket);void handle();private:int clientSocket;
};#endif // CLIENTHANDLER_H// ClientHandler.cpp
#include "ClientHandler.h"ClientHandler::ClientHandler(int clientSocket) : clientSocket(clientSocket) {}void ClientHandler::handle() {char buffer[1024];ssize_t bytesRead;while ((bytesRead = read(clientSocket, buffer, sizeof(buffer))) > 0) {std::cout << "Received: " << std::string(buffer, bytesRead) << std::endl;write(clientSocket, buffer, bytesRead); // Echo back to client}close(clientSocket);
}
3. 服务器类 (Server)
服务器类使用 epoll 进行多路复用,并利用线程池处理客户端请求。
// Server.h
#ifndef SERVER_H
#define SERVER_H#include <netinet/in.h>
#include <sys/epoll.h>
#include <vector>
#include "ThreadPool.h"
#include "ClientHandler.h"class Server {
public:Server(int port, size_t numThreads);~Server();void run();private:int serverSocket;int epollFd;ThreadPool threadPool;void acceptConnection();void handleClient(int clientSocket);static const int MAX_EVENTS = 10;
};#endif // SERVER_H// Server.cpp
#include "Server.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <iostream>Server::Server(int port, size_t numThreads) : threadPool(numThreads) {serverSocket = socket(AF_INET, SOCK_STREAM, 0);if (serverSocket == -1) {throw std::runtime_error("Failed to create socket");}int opt = 1;setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));sockaddr_in serverAddr;std::memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = INADDR_ANY;serverAddr.sin_port = htons(port);if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {throw std::runtime_error("Failed to bind socket");}if (listen(serverSocket, SOMAXCONN) == -1) {throw std::runtime_error("Failed to listen on socket");}epollFd = epoll_create1(0);if (epollFd == -1) {throw std::runtime_error("Failed to create epoll file descriptor");}epoll_event event;event.events = EPOLLIN;event.data.fd = serverSocket;if (epoll_ctl(epollFd, EPOLL_CTL_ADD, serverSocket, &event) == -1) {throw std::runtime_error("Failed to add server socket to epoll");}
}Server::~Server() {close(serverSocket);close(epollFd);
}void Server::run() {epoll_event events[MAX_EVENTS];while (true) {int numEvents = epoll_wait(epollFd, events, MAX_EVENTS, -1);if (numEvents == -1) {throw std::runtime_error("Error during epoll wait");}for (int i = 0; i < numEvents; ++i) {if (events[i].data.fd == serverSocket) {acceptConnection();} else {handleClient(events[i].data.fd);}}}
}void Server::acceptConnection() {int clientSocket = accept(serverSocket, nullptr, nullptr);if (clientSocket == -1) {std::cerr << "Failed to accept client connection" << std::endl;return;}epoll_event event;event.events = EPOLLIN | EPOLLET;event.data.fd = clientSocket;if (epoll_ctl(epollFd, EPOLL_CTL_ADD, clientSocket, &event) == -1) {std::cerr << "Failed to add client socket to epoll" << std::endl;close(clientSocket);}
}void Server::handleClient(int clientSocket) {threadPool.enqueue([clientSocket]() {ClientHandler handler(clientSocket);handler.handle();});
}
4. 主程序入口 (main.cpp)
启动服务器。
// main.cpp
#include "Server.h"int main() {try {Server server(8080, 4); // 端口 8080,4 个线程server.run();} catch (const std::exception &e) {std::cerr << "Error: " << e.what() << std::endl;return 1;}return 0;
}
说明
- 线程池:我们定义了一个
ThreadPool类,预先创建线程来处理任务,避免频繁创建和销毁线程的开销。 - 客户端处理:
ClientHandler类用于处理客户端连接,读取客户端数据并将数据回传。 - 服务器:
Server类使用epoll实现多路复用,监听新连接并将客户端请求交给线程池处理。
通过以上代码,我们创建了一个高效的 C++ 服务器,它利用 epoll 进行多路复用,并使用线程池来处理客户端请求,确保服务器的高性能和高并发处理能力。
相关文章:
如何开发高效服务(C++ )
在 C 开发高效服务器时,常用的开发模式和设计模式能够帮助你构建高效、可扩展和可维护的服务器。以下是一些常见的模式和设计模式: 1. 并发和并行编程模型 1.1 Reactor 模式 Reactor 模式是一种事件驱动设计模式,广泛用于高性能服务器编程…...
STM32实现多级菜单界面显示
1、main函数中,while循环之前 MenuStruct menu[30] //定义多级菜单结构体数组{{0,0,0,1,show0}, //第一个元素表示索引号,第二个元素表示该按键按下后要返回的界面,第三个元素表示该按键按下后要切换的菜单条目界面,第四个元素…...
Qt事件处理和传递流程
事件系统的概述 事件的类型 Qt 支持多种事件类型,每种类型代表不同的用户交互或系统事件。常见的事件类型包括: 输入事件:如鼠标事件(QMouseEvent)、键盘事件(QKeyEvent)。窗口事件ÿ…...
基于STM32移植U8g2图形库——OLED显示(HAL库)
文章目录 一、U8g2简介1、特点2、U8g2的使用步骤 二、I2C相关介绍1、I2C的基本原理2、I2C的时序协议 三、OLED屏的工作原理四、汉字点阵显示原理五、建立STM32CubeMX工程六、U8g2移植1、U8g2源码2、移植过程 七、代码编写1、参考博主实现的U82G的demo例程(1…...
C语言概述与历史
引言 C语言是一门历史悠久且影响深远的编程语言。它不仅为后继的许多编程语言奠定了基础,同时因其高效性和灵活性在系统编程和嵌入式开发领域得到了广泛应用。本篇文章将全面介绍C语言的起源与发展、设计目标与理念,以及C语言的标准演化历程,…...
钉钉Stream模式推送程序环境部署
python3.10版本需要openssl1.1.1及以上版本 参考链接:https://blog.csdn.net/weixin_42806458/article/details/110678710 wget https://www.openssl.org/source/openssl-1.1.1q.tar.gz unzip openssl-1.1.1q.tar.gz cd openssl-1.1.1q ./config --prefix/usr/loc…...
c# 二维图形绘制实践
1.等边三角形 1.1 概述 1.2 代码 using System; using System.Drawing; using System.Windows.Forms;public partial class TriangleForm : Form {public TriangleForm(){//InitializeComponent();// 确保窗体大小足够大,以容纳三角形 this.ClientSize new Siz…...
Nvidia TensorRT系列01-TensorRT的功能1
Nvidia TensorRT系列01-TensorRT的功能1 B站:肆十二-的个人空间-肆十二-个人主页-哔哩哔哩视频 (bilibili.com) 博客:肆十二-CSDN博客 问答:(10 封私信 / 72 条消息) 肆十二 - 知乎 (zhihu.com) C和Python API TensorRT的API同时支持C和Pyth…...
Vatee万腾平台:创新科技,助力企业腾飞
在全球化竞争日益激烈的今天,企业如何借助科技力量实现转型升级,已成为摆在众多企业家面前的重大课题。Vatee万腾平台凭借其卓越的创新科技和专业的服务能力,成为众多企业实现腾飞的得力助手。 一、创新科技,引领企业前行 Vatee万…...
搭建k8s集群报错unknown command “\u00a0“ for “kubeadm init“
搭建k8s报错unknown command “\u00a0” for “kubeadm init” 网上搜了一下,是因为复制过来的命令前面包含了空格,将复制的命令放到idea可以清楚看到几个命令前面有空格,删除掉就好了,记录一下...
【数据结构】三路快速排序
1. 简介 传统快速排序用的是双路快速排序,即将大于基准值的部分放到基准值右侧,小于基准值的部分放到基准值左侧,但是这种算法面对过多的重复数据的数组,时间复杂度会增多,于是就有了三路快速排序的思想,其…...
中国菜刀,蚁剑,哥斯拉,冰蝎的流量特征区别
中国菜刀、蚁剑、哥斯拉、冰蝎这四种Webshell连接工具的流量特征各有区别,以下是它们之间的主要差异: 中国菜刀(CaiDao) 流量特征: 请求包: UA头可能伪装为百度、火狐等浏览器的User-Agent。请求体中存在…...
华为OD刷题C卷 - 每日刷题32(执行任务赚积分,计算三叉搜索树的高度)
1、(执行任务赚积分): 这段代码是解决“执行任务赚积分”的问题。它提供了一个Java类Main,其中包含main方法和getResult方法,用于计算在有限的时间内,处理任务可以获得的最多积分。 main方法首先读取任务…...
QT系列教程(11) TextEdit实现Qt 文本高亮
文本高亮 对于textedit里录入的部分单词我们可以实现高亮,实现高亮主要依赖于QSyntaxHighlighter。 我们先创建一个Qt Application类,类名MainWindow, 然后新增一个C类,类名为MySyntaxHighlighter。 #ifndef MYSYNTAXHIGHLIGHTER_H #define …...
蓝队-溯源技巧
溯源技巧 大致思想 通常情况下,接到溯源任务时,获得的信息如下 攻击时间 攻击 IP 预警平台 攻击类型 恶意文件 受攻击域名/IP其中攻击 IP、攻击类型、恶意文件、攻击详情是溯源入手的点。 通过攻击类型分析攻击详情的请求包,看有没有攻击者…...
【5】JDK、JRE和JVM的区别与联系
JDK、JRE和JVM的区别与联系 Java是一种广泛使用的编程语言,它的跨平台特性得益于Java虚拟机(JVM)。然而,在Java的世界里,JDK、JRE和JVM这三个术语常常让人感到困惑。本文将阐述它们各自的功能,以及它们是如…...
【DevOps】Logstash详解:高效日志管理与分析工具
在现代软件开发和运维过程中,日志管理与分析是至关重要的环节。日志可以帮助我们追踪系统行为、诊断问题、优化性能以及确保安全合规。Logstash,作为ELK Stack(Elasticsearch、Logstash、Kibana)的核心组件之一,是一个…...
Vue3 之 Pinia 核心概念(八)
核心概念 State:这是你的应用程序的状态,是一个响应式的对象。 Getters:类似于 Vuex 中的 getters,它们是基于 state 的计算属性。 Actions:类似于 Vuex 中的 mutations 和 actions,它们用于改变 state。但…...
【办公类-04-03】华为助手导出照片视频分类(根据图片、视频的文件名日期分类导出)
背景需求: 用华为手机助手导出的照片视频,只能将jpg照片(exifread读取图片的exif拍摄日期,Png、JPEG、mp4都无法识别到exif信息) 【办公类-04-02】华为助手导出照片(jpg)读取拍摄时间分类导出…...
TVBOX 最新版下载+视频源教程
下载链接 wx 搜索 Geek 前端 发送电视资源进行获取 操作教程...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...
MySQL 数据库深度剖析:事务、SQL 优化、索引与 Buffer Pool
在当今数据驱动的时代,数据库作为数据存储与管理的核心,其性能与可靠性至关重要。MySQL 作为一款广泛使用的开源数据库,在众多应用场景中发挥着关键作用。在这篇博客中,我将围绕 MySQL 数据库的核心知识展开,涵盖事务及…...
【Redis】Redis 的持久化策略
目录 一、RDB 定期备份 1.2 触发方式 1.2.1 手动触发 1.2.2.1 自动触发 RDB 持久化机制的场景 1.2.2.2 检查是否触发 1.2.2.3 线上运维配置 1.3 检索工具 1.4 RDB 备份实现原理 1.5 禁用 RDB 快照 1.6 RDB 优缺点分析 二、AOF 实时备份 2.1 配置文件解析 2.2 开启…...
