文件结构

  • reactor_main.cpp
  • reactor_server.cpp
  • reactor_server.h
  • CMakeLists.txt

CMakeLists.txt

cmake_minimum_required(VERSION 3.10.2)

project(modern_cpp_practice)
set(CMAKE_CXX_STANDARD 17) add_executable( reactor_main reactor_main.cpp reactor_server.cpp)
target_link_libraries(reactor_main pthread)

reactor_server.h

//
// Created by kongshui on 22-4-18.
// #ifndef MODERN_CPP_PRACTICE_REACTOR_SERVER_H
#define MODERN_CPP_PRACTICE_REACTOR_SERVER_H #include <list>
#include <memory>
#include <thread>
#include <mutex>
#include <array>
#include <condition_variable> class ReactorServer
{
public:
typedef std::shared_ptr<std::thread> thread_ptr; // 设置单例
static ReactorServer &getInstance();
~ReactorServer(); ReactorServer(const ReactorServer &server) = delete;
ReactorServer &operator=(const ReactorServer &server) = delete; bool init(const std::string &ip, int16_t port);
bool stop(); bool closeClient(int client_fd); void mainLoop(); private: ReactorServer(); bool createServerListener(const std::string &ip, int16_t port); void acceptThread();
void workerThread(); private:
static constexpr int16_t recv_buf_size = 256;
static constexpr int8_t worker_thread_num = 8;
static constexpr int16_t epoll_event_num = 1024; private:
int listen_fd_ = 0;
int epoll_fd_ = 0;
bool init_ = false;
bool stop_ = true;
bool main_loop_ = true; // 一个用于接受新的客户端,一个用于接收客户端发来的数据
thread_ptr accept_thread_;
std::array<thread_ptr, worker_thread_num> worker_threads_; std::condition_variable accept_cond_;
std::mutex accept_mutex_; std::condition_variable worker_cond_;
std::mutex worker_mutex_; // 存在当前可用的客户端
std::list<int> clients_list_;
}; #endif //MODERN_CPP_PRACTICE_REACTOR_SERVER_H

reactor_server.cpp

//
// Created by kongshui on 22-4-18.
// #include "reactor_server.h" #include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <list>
#include <unistd.h>
#include <cstring>
#include <cerrno> ReactorServer::ReactorServer()
{
clients_list_.clear();
accept_thread_.reset();
for (auto &thread: worker_threads_)
thread.reset(); printf("ReactorServer\n");
} ReactorServer::~ReactorServer()
{
stop();
printf("~ReactorServer\n");
} ReactorServer &ReactorServer::getInstance()
{
static ReactorServer server;
return server;
}; bool ReactorServer::init(const std::string &ip, int16_t port)
{
if (init_)
{
printf("already init successed");
return true;
} if (!createServerListener(ip, port))
{
printf("create server listener failed\n");
return false;
} accept_thread_.reset(new std::thread(&ReactorServer::acceptThread, this)); for (auto &thread: worker_threads_)
thread.reset(new std::thread(&ReactorServer::workerThread, this)); init_ = true;
printf("init success\n"); return init_;
} bool ReactorServer::stop()
{
if (stop_)
{
printf("already stop successed");
return true;
} main_loop_ = false;
accept_cond_.notify_all();
worker_cond_.notify_all();
printf("notify all thread(accept, worker)\n"); accept_thread_->join();
for (auto &thread: worker_threads_)
thread->join();
printf("join all success(accept, worker)\n"); epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, listen_fd_, nullptr);
shutdown(listen_fd_, SHUT_RDWR);
close(listen_fd_);
close(epoll_fd_);
stop_ = true; return stop_;
} bool ReactorServer::closeClient(int client_fd)
{
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, client_fd, nullptr) == -1)
printf("close client socket failed as call epoll_ctl failed\n"); close(client_fd); return true;
} void ReactorServer::mainLoop()
{
while (main_loop_)
{
struct epoll_event ev[epoll_event_num] = {0};
int result = epoll_wait(epoll_fd_, ev, epoll_event_num, 10);
if (result == 0)
continue;
else if (result < 0)
{
printf("epoll_wait error\n");
continue;
} int num = result > epoll_event_num ? epoll_event_num : result;
for (int idx = 0; idx < num; ++idx)
{
if (ev[idx].data.fd == listen_fd_)
accept_cond_.notify_one();
else
{
{
std::unique_lock<std::mutex> guard(worker_mutex_);
clients_list_.push_back(ev[idx].data.fd);
} worker_cond_.notify_one();
}
}
} printf("main loop exit\n");
} void ReactorServer::acceptThread()
{
while (true)
{
int new_fd = -1;
socklen_t addr_len = 0;
struct sockaddr_in client_addr{};
{
std::unique_lock<std::mutex> guard(accept_mutex_);
accept_cond_.wait(guard); if (!main_loop_)
break; new_fd = accept(listen_fd_, (struct sockaddr *) &client_addr, &addr_len);
} if (new_fd == -1)
continue; printf("new client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); int old_flag = fcntl(new_fd, F_GETFL, 0);
int new_flag = old_flag | O_NONBLOCK;
if (fcntl(new_fd, F_SETFL, new_flag) == -1)
{
printf("fcntl error, old_flag = %d, new_flag = %d\n", old_flag, new_flag);
continue;
} struct epoll_event ev{};
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
ev.data.fd = new_fd;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
printf("epoll_ctl error, fd = %d\n", ev.data.fd);
} printf("accept thread exit\n");
} void ReactorServer::workerThread()
{
while (true)
{
int client_fd = -1;
{
std::unique_lock<std::mutex> gurad(worker_mutex_);
while (clients_list_.empty())
{
if (!main_loop_)
{
printf("worker thread exit\n");
return;
} worker_cond_.wait(gurad);
} client_fd = clients_list_.front();
clients_list_.pop_front();
} std::string client_msg;
bool error = false;
char buf[recv_buf_size] = {0}; while (true)
{
memset(buf, 0, sizeof(buf));
int result = recv(client_fd, buf, recv_buf_size, 0);
if (result == -1)
{
if (errno == EWOULDBLOCK)
break;
else
{
printf("recv error, client disconnected, fd = %d\n", client_fd);
closeClient(client_fd);
error = true;
break;
}
} else if (result == 0)
{
printf("peer closed, client disconnected, fd = %d\n", client_fd);
closeClient(client_fd);
error = true;
break;
} client_msg += buf;
} if (error)
continue; printf("client msg: %s\n", client_msg.c_str()); client_msg += " test send"; int result = send(client_fd, client_msg.c_str(), client_msg.length(), 0);
if (result == -1)
{
if (errno == EWOULDBLOCK)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
} else
{
printf("send error, fd = %d\n", client_fd);
closeClient(client_fd);
break;
} }
}
} bool ReactorServer::createServerListener(const std::string &ip, int16_t port)
{
listen_fd_ = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (listen_fd_ == -1)
return false; int on = 1;
setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof(on)); struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip.c_str());
server_addr.sin_port = htons(port); if (bind(listen_fd_, (sockaddr *) &server_addr, sizeof(server_addr)) == -1)
{
printf("bind failed\n");
return false;
} if (listen(listen_fd_, 50) == -1)
{
printf("listen failed\n");
return false;
} epoll_fd_ = epoll_create(1);
if (epoll_fd_ == -1)
{
printf("epoll_create failed\n");
return false;
} struct epoll_event ev{};
ev.events = EPOLLIN | EPOLLRDHUP;
ev.data.fd = listen_fd_;
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, listen_fd_, &ev) == -1)
return false; return true;
}

reactor_main.cpp

//
// Created by kongshui on 22-4-18.
// #include "reactor_server.h" int main()
{
if (!ReactorServer::getInstance().init("0.0.0.0", 3333))
return -1; ReactorServer::getInstance().mainLoop(); return 0;
}

参考链接

Linux C++ Reactor模式的更多相关文章

  1. 【Linux 网络编程】生动讲解 Reactor 模式与 Proactor 模式

    五种 I/O 模型 先花费点时间了解这几种 I/O 模型,有助于后面的理解. 阻塞 I/O 与非阻塞 I/O 阻塞和非阻塞的概念能应用于所有的文件描述符,而不仅仅是 socket.我们称阻塞的文件描述 ...

  2. 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式

    为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...

  3. Reactor模式详解

    转自:http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html 前记 第一次听到Reactor模式是三年前的某个晚上,一个室友突然跑过 ...

  4. Reactor模式解析——muduo网络库

    最近一段时间阅读了muduo源码,读完的感受有一个感受就是有点乱.当然不是说代码乱,是我可能还没有完全消化和理解.为了更好的学习这个库,还是要来写一些东西促进一下. 我一边读一边尝试在一些地方改用c+ ...

  5. Reactor模式

    对象行为类的设计模式,对同步事件分拣和派发.别名Dispatcher(分发器) Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I ...

  6. C++服务器设计(一):基于I/O复用的Reactor模式

    I/O模型选择 在网络服务端编程中,一个常见的情景是服务器需要判断多个已连接套接字是否可读,如果某个套接字可读,则读取该套接字数据,并进行进一步处理. 在最常用的阻塞式I/O模型中,我们对每个连接套接 ...

  7. 公布一个基于 Reactor 模式的 C++ 网络库

    公布一个基于 Reactor 模式的 C++ 网络库 陈硕 (giantchen_AT_gmail) Blog.csdn.net/Solstice 2010 Aug 30 本文主要介绍 muduo 网 ...

  8. libevent之Reactor模式

    通过前边的一篇博文轻量级网络库libevent初探,我们知道libevent实际上是封装了不同操作系统下的/dev/poll.kqueue.event ports.select.poll和epoll事 ...

  9. 也谈Reactor模式

    何谓Reactor模式?它是实现高性能IO的一种设计模式.网上资料有很多,有些写的也很好,但大多不知其所以然.这里博主按自己的思路简单介绍下,有不对的地方敬请指正. BIO Java1.4(2002年 ...

随机推荐

  1. Mysql之锁(一)

    读锁与写锁 读锁:共享锁.Shared Locks.S锁. 写锁:排他锁.Exclusive Locks.X锁. 读锁:只能读不允许写 写锁:不能读也不能写,只允许自己写 但是允许其他事务进行普通的s ...

  2. Redis的集群搭建(四)

    1.redis-cluster架构图 2.redis-cluster投票:容错 架构细节: (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2) ...

  3. 你如何确保 main()方法所在的线程是 Java 程序最后结束 的线程?

    我们可以使用 Thread 类的 join()方法来确保所有程序创建的线程在 main()方法退出前结束.

  4. Kube-OVN:大型银行技术团队推荐的金融级云原生网络方案

    近日,由TWT社区主办的2021容器云职业技能大赛团队赛的冠军作品:<适用于大中型银行的云原生技术体系建设方案>中,Kube-OVN成为银行技术团队推荐的金融级云原生网络最佳实践.本文部分 ...

  5. ML、DL及RL介绍和区别

    前言 在刚入门的时候,我们很容易被这些词语弄得眼花缭乱,如人工智能.机器学习.深度学习及强化学习等等.它们之间互相联系,却有一定区别.我们学习人工智能AI,需要对这些词语有一定的概念,不然,研究了这么 ...

  6. 4.2 ROS节点运行管理launch文件

    4.2 ROS节点运行管理launch文件 关于 launch 文件的使用我们已经不陌生了,在第一章内容中,就曾经介绍到: 一个程序中可能需要启动多个节点,比如:ROS 内置的小乌龟案例,如果要控制乌 ...

  7. 如何监控微信小程序HTTP请求错误

    摘要: Fundebug的微信小程序错误监控插件更新至0.5.0,支持监控HTTP请求错误. 接入插件 接入Fundebug的错误监控插件非常简单,只需要下载fundebug.0.5.0.min.js ...

  8. C# 委托专题

    单播委托:一个委托只指向一个方法: 多播委托:一个委托指向多个方法,形成一个方法链: Main是静态方法,里面只能引用静态方法,而不能引用实例方法: Main可以进行类的实例化,然后引用实例化后的方法 ...

  9. mysql基本操作1

    数据库的分类 --1.关系型数据库-----用"表"保存数据,相关数据存入一张表中   --2.非关系型数据库-----键值数据库-----对象数据库 ###主流关系型数据库-Or ...

  10. Python实现简单用户注册信息管理系统

    运行效果: 注意:运行前请在同一目录下创建一个userdata.bin用于保存用户数据 源代码: 1 # coding:utf-8 2 ''' 3 用户注册信息管理系统 4 功能包括: 5 1.查看全 ...