高级IO_多路转接之ET模式Reactor

文章目录


提示:以下是本篇文章正文内容,下面案例可供参考

Reactor是什么?

Reactor模式是一种事件驱动的并发模型,它通过将事件处理逻辑与事件分发机制解耦,实现高性能、可扩展的并发处理。Reactor模式适用于大量短时连接或需要高效I/O处理的场景,如Web服务器、聊天服务器等。

今天我们所实现的Reactor是基于ET模式下的多路转接模式。

LT模式 VS ET模式

以我们的epoll为例,我们的epoll默认是LT模式。

LT模式:只要有事件就绪,就会不断提醒。

ET模式:只有事件从无到有,从少到多的情况,才会提醒一次

所以对于ET模式而言,就需要逼服务器一次性将所有缓冲区数据全部读完,也算是逼着你效率提高。 而只提醒一次也是高效的表现。

示例代码

cpp 复制代码
#include "Epoll.hpp"
#include "Socket.hpp"
#include <vector>
#include <functional>
#include <unordered_map>
#include "Common.hpp"
#include "Calculator.hpp"

class Connection;
class ReactorServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;
const std::string default_ip = "0.0.0.0";
const uint16_t default_port = 8080;

#define EVENT_IN (EPOLLIN | EPOLLET)
#define EVENT_OUT (EPOLLOUT | EPOLLET)

void SetNonBlock(int fd)
{
    int fl = fcntl(fd, F_GETFL);
    if (fl < 0)
    {
        // 获取失败
        perror("F_GETFD Error");
        return;
    }
    int n = fcntl(fd, F_SETFL, fl | O_NONBLOCK);
    if (n < 0)
    {
        perror("Set Nonblock Error");
    }
    else
    {
        lg(Info, "Set Nonblock Succeed, Fd: %d", fd);
        // std::cout << "Fd:" << fd << " ,set nonblock done" << std::endl;
    }
}

class Connection : public nocopy
{
public:
    Connection(int sockfd, ReactorServer *reactor_server)
        : _sockfd(sockfd), _reactor_server(reactor_server) {}

    void SetHandle(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    int Getfd()
    {
        return _sockfd;
    }

    std::string &GetInBuffer()
    {
        return _inbuffer;
    }

    std::string &GetOutBuffer()
    {
        return _outbuffer;
    }

    ~Connection() {}

private:
    int _sockfd;
    std::string _inbuffer;
    std::string _outbuffer;

    ReactorServer *_reactor_server;

public:
    func_t _recv_cb;
    func_t _send_cb;
    func_t _except_cb;
};

class ReactorServer
{
private:
    const int num = 128;

public:
    ReactorServer(uint16_t port, func_t handle_message)
        : _port(port), _listensock(new Socket), _epoller(new Epoller), _handle_message(handle_message) {}

    void EnableEvent(int fd, bool recv, bool send)
    {
        int events = 0;
        events |= (recv ? EVENT_IN : 0);
        events |= (send ? EVENT_OUT : 0);
        _epoller->EpollerUpdate(EPOLL_CTL_MOD, fd, events);
    }

    void Accepter(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            int newsock = _listensock->Accept();
            if (newsock < 0)
            {
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                lg(Warning, "Accept Error...");
                break;
            }
            AddConnetion(newsock, EVENT_IN, std::bind(&ReactorServer::Recver, this, std::placeholders::_1),
                         std::bind(&ReactorServer::Sender, this, std::placeholders::_1),
                         std::bind(&ReactorServer::Excepter, this, std::placeholders::_1));
        }
    }

    void Recver(std::shared_ptr<Connection> connection)
    {
        int fd = connection->Getfd();
        while (true)
        {
            char buffer[1024];
            memset(buffer, 0, sizeof buffer);
            int n = recv(connection->Getfd(), buffer, sizeof buffer - 1, 0);
            if (n > 0)
            {
                connection->GetInBuffer() += buffer;
                std::cout << connection->GetInBuffer();
            }
            else if (n < 0)
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                lg(Warning, "Read Error...");
                connection->_except_cb(connection);
                return;
            }
            else
            {
                lg(Info, "Foreign Host Closed...");
                connection->_except_cb(connection);
                return;
            }
        }
        _handle_message(connection);
    }

    void Sender(std::shared_ptr<Connection> connection)
    {
        int fd = connection->Getfd();
        std::string &mes = connection->GetOutBuffer();
        while (1)
        {
            int n = send(fd, mes.c_str(), mes.size(), 0);
            if (n < 0)
            {
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                connection->_except_cb(connection);
                return;
            }
            if (n == 0)
            {
                break;
            }
            mes.erase(0, n);
            if (mes.empty())
            {
                break;
            }
        }
        if (!mes.empty())
        {
            EnableEvent(fd, true, true);
        }
        else
        {
            EnableEvent(fd, true, false);
        }
    }

    void Excepter(std::shared_ptr<Connection> connection)
    {
        int fd = connection->Getfd();
        // 1.先从内核中移除
        _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
        // 2.从_connections中移除
        _connections.erase(fd);
        // 3.关闭sockfd
        close(fd);
        lg(Info, "Sockfd: %d Closed...", fd);
    }

    void AddConnetion(int fd, int events, func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        // 1.设置非阻塞
        SetNonBlock(fd);
        // 2.创建新connection
        std::shared_ptr<Connection> newcon(new Connection(fd, this));
        newcon->SetHandle(recv_cb, send_cb, except_cb);
        // 3.插入到_connetcions
        _connections[fd] = newcon;
        // 4.放入内核
        _epoller->EpollerUpdate(EPOLL_CTL_ADD, fd, events);
    }

    void Init()
    {
        _epoller->Init();
        _listensock->Init();
        _listensock->Bind(AF_INET, default_ip, _port);
        _listensock->Listen();
        AddConnetion(_listensock->_sockfd, EVENT_IN, std::bind(&ReactorServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }

    bool IsConnectionSafe(int fd)
    {
        return _connections.find(fd) == _connections.end() ? false : true;
    }

    void Start()
    {
        struct epoll_event recvs[num];
        while (1)
        {
            int n = _epoller->EpollWait(recvs, num, -1);
            if (n > 0)
            {
                // std::cout << "检测到事件 n:" << n << std::endl;
                for (int i = 0; i < n; i++)
                {
                    int fd = recvs[i].data.fd;
                    int events = recvs[i].events;
                    if (events & EPOLLERR)
                    {
                        events |= EPOLLIN;
                    }
                    if (events & EPOLLHUP)
                    {
                        events |= EPOLLIN;
                    }
                    if ((events & EPOLLIN) && IsConnectionSafe(fd))
                    {
                        if (_connections[fd]->_recv_cb)
                        {
                            _connections[fd]->_recv_cb(_connections[fd]);
                        }
                    }
                    if ((events & EPOLLOUT) && IsConnectionSafe(fd))
                    {
                        if (_connections[fd]->_send_cb)
                        {
                            _connections[fd]->_send_cb(_connections[fd]);
                        }
                    }
                }
            }
            else if (n == 0)
            {
                lg(Info, "Time Out...");
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    continue;
                lg(Warning, "Epoll error...");
                std::cout << "errno:" << errno << " strerror:" << strerror(errno) << std::endl;
                exit(1);
            }
        }
    }
    ~ReactorServer() {}

private:
    std::unique_ptr<Socket> _listensock;
    std::unique_ptr<Epoller> _epoller;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    uint16_t _port;
    func_t _handle_message;
};
cpp 复制代码
#include "ReactorServer.hpp"


void default_HandleMessage(std::shared_ptr<Connection> connection)
{
//根据服务器的服务内容编写此函数
//例如我这里想做一个计算器服务
    Calculator cal;
    std::string &inbuffer = connection->GetInBuffer();
    while (!inbuffer.empty())
    {
        std::string mes;
        int type;
        if (!CheckType(inbuffer, &type))
        {
            // 报文内容出现问题
            inbuffer = "";
            break;
        }
        if (type == 1)
        {
            IntHandle(inbuffer, &mes);
        }
        else if (type == 2)
        {
            DoubleHandle(inbuffer, &mes);
        }
        else
        {
            lg(Warning, "Type Error, type: %d ...", type);
        }
        connection->GetOutBuffer() += mes;
        connection->_send_cb(connection);
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cout << "Usage: ./selectServer port[8000-9000]" << std::endl;
    }
    ReactorServer ser(atoi(argv[1]), func_t(default_HandleMessage));
    ser.Init();
    ser.Start();

    return 0;
}
相关推荐
2401_8504108313 分钟前
文件系统和日志管理
linux·运维·服务器
XMYX-01 小时前
使用 SSH 蜜罐提升安全性和记录攻击活动
linux·ssh
二十雨辰3 小时前
[linux]docker基础
linux·运维·docker
饮浊酒4 小时前
Linux操作系统 ------(3.文本编译器Vim)
linux·vim
lihuhelihu4 小时前
第3章 CentOS系统管理
linux·运维·服务器·计算机网络·ubuntu·centos·云计算
矛取矛求4 小时前
Linux系统性能调优技巧
linux
One_Blanks4 小时前
渗透测试-Linux基础(1)
linux·运维·安全
Perishell4 小时前
无人机避障——大疆与Airsim中的角速度信息订阅获取
linux·动态规划·无人机
爱吃喵的鲤鱼4 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
dessler4 小时前
Linux系统-ubuntu系统安装
linux·运维·云计算