Linux C++ Reactor模式

文件结构

  • 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;
}

参考链接

posted @ 2022-04-18 17:43  空水  阅读(611)  评论(0编辑  收藏  举报