如何开发高效服务(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 前端 发送电视资源进行获取 操作教程...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
