文件结构

  • 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. nginx反向代理失败,又是 fastdfs 的锅

    fdfs问题记录 [root@hostcad logs]# systemctl status fdfs_trackerd.service ● fdfs_trackerd.service - LSB: ...

  2. Serial 与 Parallel GC 之间的不同之处?

    Serial 与 Parallel 在 GC 执行的时候都会引起 stop-the-world.它们之间主要 不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而 para ...

  3. 学习Apache(四)

    介绍 Apache HTTP 服务器被设计为一个功能强大,并且灵活的 web 服务器, 可以在很多平台与环境中工作.不同平台和不同的环境往往需要不同 的特性,或可能以不同的方式实现相同的特性最有效率. ...

  4. 4.RDD操作

    目录 一. RDD创建 从本地文件系统中加载数据创建RDD 从HDFS加载数据创建RDD 通过并行集合(列表)创建RDD 二. RDD操作 转换操作 filter(func) map(func) fl ...

  5. 4-Pandas数据预处理之数据转换(df.map()、df.replace())

    在数据分析中,根据需求,有时候需要将一些数据进行转换,而在Pandas中,实现数据转换的常用方法有: 利用函数或是映射 可以将自己定义的或者是其他包提供的函数用在Pandas对象上实现批量修改. ap ...

  6. VC 下如何正确的创建及管理项目

    讲解 VC 下如何正确的创建及管理项目 本文讲解 Visual C++ 的项目文件组成,以及如何正确的创建及管理项目. 本文所设计的内容是初学者必须要掌握的.不能正确的管理项目,就不能进一步写有规模的 ...

  7. 使用Bootstrap typeahead插件实现搜索框自动补全的配置参数。

    示例代码: <input type="text" id="addr"/> <input type="text" hidde ...

  8. 实现自定义的小程序底部tabbar

    背景 诶,当然是为了实现更有温度的代码啦(背后设计师拿着刀对着我) 自带tabbar app.json中配置: tabBar: { backgroundColor: '#fff', borderSty ...

  9. Bitmap图片的处理

      一.View转换为Bitmap 在Android中所有的控件都是View的直接子类或者间接子类,通过它们可以组成丰富的UI界面.在窗口显示的时候Android会把这些控件都加载到内存中,形成一个以 ...

  10. Virtual Function(虚函数)in c++

    Virtual Function(虚函数)in c++ 用法: virtual void log() { std::cout << "hello world!" < ...