【Linux网络编程】15. Reactor 反应堆模式

文章目录

一、介绍

1、整体架构概述

这里实现的是一个基于 epoll + ET 边缘触发Reactor 反应堆高性能服务器。

它是高并发网络编程的标准设计模式 ,专门用来处理海量连接、少量活跃的场景。

核心思想:

  • 一个线程负责事件监听(epoll)
  • 所有 IO 事件统一分发
  • 每个连接自己处理读、写、异常
  • 业务逻辑与网络框架完全解耦

二、Reactor 服务器

代码结构:

cpp 复制代码
Mutex.hpp
Log.hpp
Common.hpp
InetAddr.hpp
Socket.hpp
Epoller.hpp
Connection.hpp
Reactor.hpp
Channel.hpp
Listener.hpp
NetCal.hpp
Main.cc
Client.cc

makefile

c 复制代码
.PHONY:all
all:client reactorserver

reactorserver:Main.cc
	g++ -o $@ $^ -ljsoncpp -std=c++17
client:Client.cc
	g++ -o $@ $^ -ljsoncpp -std=c++17

.PHONY:clean
clean:
	rm -f reactorserver client

1、基础工具模块

1)Mutex.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <pthread.h>

namespace MutexModule
{
    // 互斥锁封装类
    class Mutex
    {
    public:
        // 初始化锁
        Mutex()
        {
            pthread_mutex_init(&_mutex, nullptr);
        }

        // 加锁
        void Lock()
        {
            int n = pthread_mutex_lock(&_mutex);
        }

        // 解锁
        void Unlock()
        {
            int n = pthread_mutex_unlock(&_mutex);
        }

        // 获取锁指针
        pthread_mutex_t *Get()
        {
            return &_mutex;
        }

        // 销毁锁
        ~Mutex()
        {
            pthread_mutex_destroy(&_mutex);
        }

    private:
        pthread_mutex_t _mutex;
    };

    // RAII 自动加解锁
    class LockGuard
    {
    public:
        LockGuard(Mutex &mutex) : _mutex(mutex)
        {
            _mutex.Lock(); // 构造时加锁
        }

        ~LockGuard()
        {
            _mutex.Unlock(); // 析构时解锁
        }

    private:
        Mutex &_mutex;
    };
}

2)Log.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <memory>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
#include "Mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;

    const std::string gsep = "\r\n";

    // 日志策略基类(接口)
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void SyncLog(const std::string &message) = 0;
    };

    // 控制台日志输出(线程安全)
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        void SyncLog(const std::string &message) override
        {
            LockGuard lockguard(_mutex);
            std::cout << message << gsep;
        }

        ~ConsoleLogStrategy() {}

    private:
        Mutex _mutex;
    };

    const std::string defaultpath = "./log/"; //   /var/log
    const std::string defaultfile = "my.log";

    // 文件日志输出(自动建目录、线程安全)
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
            : _path(path), _file(file)
        {
            LockGuard lockguard(_mutex);
            if (std::filesystem::exists(_path))
                return;

            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << std::endl;
            }
        }

        // 追加写入日志文件
        void SyncLog(const std::string &message) override
        {
            LockGuard lockguard(_mutex);
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(filename, std::ios::app);
            if (!out.is_open())
                return;

            out << message << gsep;
            out.close();
        }

    private:
        std::string _path;
        std::string _file;
        Mutex _mutex;
    };

    // 日志等级类
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    // 日志等级转字符串
    std::string LevelStr(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }

    // 获取格式化时间字符串(线程安全)
    std::string GetTimeStamp()
    {
        time_t curr = time(nullptr);
        struct tm curr_tm; // 出参
        localtime_r(&curr, &curr_tm);
        char buf[128]; // 出参
        snprintf(buf, sizeof(buf), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr_tm.tm_year + 1900,
                 curr_tm.tm_mon + 1,
                 curr_tm.tm_mday,
                 curr_tm.tm_hour,
                 curr_tm.tm_min,
                 curr_tm.tm_sec);
        return buf;
    }

    // 日志核心管理类
    class Logger 
    {
    public:
        Logger()
        {
            EnableConsoleLogStrategy();
        }

        // 切换为文件输出
        void EnableFileLogStrategy()
        {
            _fflush_strategy = std::make_unique<FileLogStrategy>();
        }

        // 切换为控制台输出
        void EnableConsoleLogStrategy()
        {
            _fflush_strategy = std::make_unique<ConsoleLogStrategy>();
        }

        // 日志消息构造: 负责拼接内容, 析构时自动输出
        class LogMessage
        {
        public:
            // 构造日志头部(时间、等级、进程ID、文件名、行号)
            LogMessage(LogLevel level, std::string src_name, int line_number, Logger &logger)
                :  _logger(logger) 
            {
                std::stringstream ss;
                ss << "[" << GetTimeStamp() << "] "
                   << "[" << LevelStr(level) << "] "
                   << "[" << getpid() << "] "
                   << "[" << src_name << "] "
                   << "[" << line_number << "] - ";
                _loginfo = ss.str(); 
            }

            // 流方式拼接日志内容
            template <typename T>
            LogMessage &operator<<(const T &info)
            {
                std::stringstream ss;
                ss << info;           
                _loginfo += ss.str(); 
                return *this;         
            }

            // 析构自动输出日志
            ~LogMessage()
            {
                if (_logger._fflush_strategy) 
                {
                    _logger._fflush_strategy->SyncLog(_loginfo);
                }
            }

        private:     
            std::string _loginfo;   
            Logger &_logger;        
        };

        // 仿函数接口, 创建日志消息
        LogMessage operator()(LogLevel level, std::string name, int line)
        {
            return LogMessage(level, name, line, *this);
        }

    private:
        std::unique_ptr<LogStrategy> _fflush_strategy;
    };

    Logger logger; 

// 简化调用宏
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}

3)Common.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <fcntl.h>

// 程序退出状态码枚举
enum ExitCode
{
    OK = 0,           // 正常
    USAGE_ERR,        // 参数错误
    SOCKET_ERR,       // Socket 创建失败
    BIND_ERR,         // 绑定失败
    LISTEN_ERR,       // 监听失败
    CONNECT_ERR,      // 连接失败
    FORK_ERR,         // 进程创建失败
    OPEN_ERR,         // 打开文件失败
    EPOLL_CREATE_ERR, // epoll创建失败
    EPOLL_CTL_ERR     // epoll控制失败
};

// 禁止拷贝基类(继承后无法拷贝/赋值)
class NoCopy
{
public:
    NoCopy() {}
    ~NoCopy() {}

    NoCopy(const NoCopy &) = delete;
    NoCopy &operator=(const NoCopy &) = delete;
};

// 地址类型转换: sockaddr_in → sockaddr*
#define CONV(addr) ((struct sockaddr *)&addr)

int defaultport = 8080; // 默认端口

// 设置fd为非阻塞
void SetNonBlock(int fd)
{
    int f1 = fcntl(fd, F_GETFL);
    if (f1 < 0)
    {
        return;
    }
    fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}

// 非阻塞accept返回状态
#define ACCEPT_ERR -1      // 错误
#define ACCEPT_CONTINUE -2 // 继续
#define ACCEPT_DONE -3     // 完成

4)InetAddr.hpp

cpp 复制代码
#pragma once
#include "Common.hpp"

// IPv4地址封装: 主机格式 <-> 网络格式
class InetAddr
{
public:
	// 默认构造
    InetAddr() {}
    
    // 构造: 网络地址 -> 主机格式
    InetAddr(struct sockaddr_in &addr)
        : _addr(addr)
    {
        _port = ntohs(_addr.sin_port); // port
        char ipbuffer[64];             // ip
        inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(ipbuffer));
        _ip = ipbuffer;
    }

    // 构造:指定IP+端口 → 网络格式
    InetAddr(const std::string &ip, uint16_t port)
        : _ip(ip), _port(port)
    {
        memset(&_addr, 0, sizeof(_addr));
        _addr.sin_family = AF_INET;
        inet_pton(AF_INET, _ip.c_str(), &_addr.sin_addr); // ip
        _addr.sin_port = htons(_port);                    // port
    }

    // 构造:仅端口, 绑定本机所有IP
    InetAddr(uint16_t port)
        : _port(port), _ip()
    {
        memset(&_addr, 0, sizeof(_addr));
        _addr.sin_family = AF_INET;
        _addr.sin_addr.s_addr = INADDR_ANY; // ip
        _addr.sin_port = htons(_port);      // port
    }
		
	// 设置网络地址信息
    void SetAddr(struct sockaddr_in &addr)
    {
        _addr = addr; 
        _port = ntohs(_addr.sin_port); 

        char ipbuffer[64];
        inet_ntop(AF_INET, &_addr.sin_addr, ipbuffer, sizeof(ipbuffer));
        _ip = ipbuffer;
    }
    
    // 获取点分十进制IP
    std::string Ip() { return _ip; }

    // 获取主机字节序端口
    uint16_t Port() { return _port; }

    // 获取原生网络地址结构体
    const struct sockaddr_in &NetAddr() { return _addr; }

    // 获取通用地址指针(用于系统调用)
    const struct sockaddr *NetAddrPtr() { return CONV(_addr); }

    // 获取地址长度
    socklen_t NetAddrLen() { return sizeof(_addr); }

    // 比较两个地址是否相同
    bool operator==(const InetAddr &addr)
    {
        return addr._ip == _ip && addr._port == _port;
    }

    // 转为 ip:port 格式字符串
    std::string StringAddr()
    {
        return _ip + ":" + std::to_string(_port);
    }

    ~InetAddr() {}

private:
    struct sockaddr_in _addr; // 网络字节序地址
    std::string _ip;          // 点分十进制IP
    uint16_t _port;           // 主机字节序端口
};

2、网络核心模块

1)Socket.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

namespace SocketModule
{
    using namespace LogModule;
    const static int gbacklog = 16; // 监听队列长度

    // 抽象Socket接口(模板方法)
    class Socket
    {
    public:
        virtual ~Socket() {}
        virtual void SocketOrDie() = 0;                                       // 创建socket
        virtual void BindOrDie(uint16_t port) = 0;                            // 绑定端口
        virtual void ListenOrDie(int backlog) = 0;                            // 监听
        virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;         // 接收连接
        virtual void Close() = 0;                                             // 关闭socket
        virtual int Recv(std::string *out) = 0;                               // 接收数据
        virtual int Send(const std::string &message) = 0;                     // 发送数据
        virtual int Connect(const std::string &server_ip, uint16_t port) = 0; // 连接服务端
        virtual int Fd() = 0;                                                 // 获取文件描述符
        
    public:
        // 构建TCP服务端
        void BuildTcpSocketMethod(uint16_t port, int backlog = gbacklog)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie(backlog);
        }

        // 构建TCP客户端
        void BuildTcpClientSocketMethod()
        {
            SocketOrDie();
        }
    };

    const static int defaultfd = -1;

    // TCP套接字实现
    class TcpSocket : public Socket
    {
    public:
        TcpSocket() : _sockfd(defaultfd) {}

        TcpSocket(int fd) : _sockfd(fd) {}

        ~TcpSocket() {}

        // 创建socket
        void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "socket error";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "socket success";
        }

        // 绑定端口
        void BindOrDie(uint16_t port) override
        {
            InetAddr localaddr(port);
            int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind error";
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "bind success";
        }

        // 监听
        void ListenOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen error";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "listen success";
        }

        // 接收客户端连接
        std::shared_ptr<Socket> Accept(InetAddr *client) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            // 阻塞等待客户端连接
            int fd = ::accept(_sockfd, CONV(peer), &len);
            if (fd < 0)
            {
                LOG(LogLevel::WARNING) << "accept warning ...";
                return nullptr;
            }

            // 保存客户端地址
            client->SetAddr(peer);

            // 返回新连接的socket对象
            return std::make_shared<TcpSocket>(fd);
        }

        // 接收数据  出参out
        int Recv(std::string *out) override
        {
            char buffer[1024];
            ssize_t n = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                *out += buffer;
            }
            return n;
        }

        // 发送数据
        int Send(const std::string &message) override
        {
            return send(_sockfd, message.c_str(), message.size(), 0);
        }

        // 连接服务端
        int Connect(const std::string &server_ip, uint16_t port) override
        {
            InetAddr server(server_ip, port);
            return ::connect(_sockfd, server.NetAddrPtr(), server.NetAddrLen());
        }

        // 关闭套接字
        void Close() override
        {
            if (_sockfd >= 0)
                ::close(_sockfd);
        }
		
		// 获取文件描述符
		int Fd() override
        {
            return _sockfd;
        }
        
    private:
        int _sockfd; // socket文件描述符
    };
}

2)Epoller.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Common.hpp"

using namespace LogModule;

// Epoll 封装类: 统一管理创建、增删改事件、等待
class Epoller
{
public:
    // 构造: 创建 epoll 实例
    Epoller() : _epfd(-1)
    {
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_create error!!!";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "epoll_create success: " << _epfd;
    }

    // 事件操作辅助函数: 增/删/改
    void ModEventHelper(int sockfd, uint32_t events, int oper)
    {
        struct epoll_event ev;
        ev.events = events;
        ev.data.fd = sockfd;

        int n = epoll_ctl(_epfd, oper, sockfd, &ev);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_ctl error";
            return;
        }
        LOG(LogLevel::INFO) << "epoll_ctl success: " << sockfd;
    }

    // 添加事件
    void AddEvent(int sockfd, uint32_t events)
    {
        ModEventHelper(sockfd, events, EPOLL_CTL_ADD);
    }

    // 删除事件
    void DelEvent(int sockfd)
    {
        int n = epoll_ctl(sockfd, EPOLL_CTL_DEL, sockfd, nullptr);
    }

    // 修改事件
    void ModEvent(int sockfd, uint32_t events)
    {
        ModEventHelper(sockfd, events, EPOLL_CTL_MOD);
    }

    // 等待事件就绪
    int WaitEvents(struct epoll_event revs[], int maxnum, int timeout)
    {
        int n = epoll_wait(_epfd, revs, maxnum, timeout);
        if (n < 0)
            LOG(LogLevel::WARNING) << "epoll_wait error";
        else if (n == 0)
            LOG(LogLevel::WARNING) << "epoll_wait timeout";

        return n;
    }

    // 析构:关闭 epoll fd
    ~Epoller()
    {
        if (_epfd >= 0)
            close(_epfd);
    }

private:
    int _epfd; // epoll 文件描述符
};

3)Connection.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <functional>
#include "InetAddr.hpp"

class Reactor;

// 业务回调函数类型: 处理客户端消息
using handler_t = std::function<std::string(std::string &)>;

// 连接基类: 封装 fd + 事件 + 回调
class Connection
{
public:
    Connection()
        : _events(0),
          _owner(nullptr)
    {
    }

    // 设置监听事件
    void SetEvent(const uint32_t &events)
    {
        _events = events;
    }

    // 获取监听事件
    uint32_t GetEvent()
    {
        return _events;
    }

    // 设置所属 Reactor
    void SetOwner(Reactor *owner)
    {
        _owner = owner;
    }

    // 获取所属 Reactor
    Reactor *GetOwner()
    {
        return _owner;
    }

    // 注册业务回调
    void RegisterHandler(handler_t handler)
    {
        _handler = handler;
    }

    // 纯虚函数: 子类必须实现
    virtual void Recver() = 0;   // 读事件处理
    virtual void Sender() = 0;   // 写事件处理
    virtual void Excepter() = 0; // 异常处理
    virtual int GetSockFd() = 0; // 获取套接字

    ~Connection()
    {
    }

public:
    handler_t _handler; // 业务回调函数

private:
    uint32_t _events; // 监听的事件类型
    Reactor *_owner;  // 所属反应堆
};

4)Reactor.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <memory>
#include <unordered_map>

#include "Log.hpp"
#include "Epoller.hpp"
#include "Connection.hpp"

using namespace LogModule;

// Reactor 反应堆: 事件监听 + 分发 + 连接管理
class Reactor
{
    static const int revs_num = 128;

private:
    // 判断连接是否存在(内部)
    bool IsConnectionExistsHelper(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        return iter != _connections.end();
    }

    // 判断连接是否存在(智能指针)
    bool IsConnectionExists(const std::shared_ptr<Connection> &conn)
    {
        return IsConnectionExistsHelper(conn->GetSockFd());
    }

    // 判断连接是否存在(sockfd)
    bool IsConnectionExists(int sockfd)
    {
        return IsConnectionExistsHelper(sockfd);
    }

    // 无连接
    bool IsConnectionEmpty()
    {
        return _connections.empty();
    }

    // 等待一次就绪事件
    int LoopOnce(int timeout)
    {
        return _epoller_ptr->WaitEvents(_revs, revs_num, timeout);
    }

    // 事件分发
    void Dispatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            // 异常事件统一当读写处理
            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);

            // 读事件
            if (revents & EPOLLIN && IsConnectionExists(sockfd))
                _connections[sockfd]->Recver();

            // 写事件
            if (revents & EPOLLOUT && IsConnectionExists(sockfd))
                _connections[sockfd]->Sender();
        }
    }

public:
    Reactor()
        : _epoller_ptr(std::make_unique<Epoller>()),
          _isrunning(false)
    {
    }

    // 主循环
    void Loop()
    {
        if (IsConnectionEmpty())
            return;

        _isrunning = true;
        int timeout = -1; // 永久非阻塞
        while (_isrunning)
        {
            PrintConnection();
            int n = LoopOnce(timeout);
            Dispatcher(n);
        }
        _isrunning = false;
    }

    // 添加连接: 加入 epoll + 加入 map
    void AddConnection(std::shared_ptr<Connection> &conn)
    {
        if (IsConnectionExists(conn))
        {
            LOG(LogLevel::WARNING) << "conn exists: " << conn->GetSockFd();
            return;
        }

        // 加入 epoll
        uint32_t events = conn->GetEvent();
        int sockfd = conn->GetSockFd();
        _epoller_ptr->AddEvent(sockfd, events);

        // 设置所属 Reactor
        conn->SetOwner(this);

        // 加入管理 map
        _connections[sockfd] = conn;
    }

    // 动态开关读写事件
    void EnableReadWrite(int sockfd, bool enableread, bool enablewrite)
    {
        if (!IsConnectionExists(sockfd))
        {
            LOG(LogLevel::WARNING) << "EnableReadWrite: conn not exists: " << sockfd;
            return;
        }

        // 设置 ET + 读写事件
        uint32_t new_event = EPOLLET;
        if(enableread)
            new_event |= EPOLLIN;
        if(enableread)
            new_event |= EPOLLOUT;

        _connections[sockfd]->SetEvent(new_event);
        _epoller_ptr->ModEvent(sockfd, new_event);
    }

    // 删除连接: 从epoll移除 + 关闭fd
    void DelConnection(int sockfd)
    {
        _epoller_ptr->DelEvent(sockfd);
        _connections.erase(sockfd);
        close(sockfd);
        LOG(LogLevel::INFO) << "client quit: " << sockfd;
    }

    // 停止反应堆
    void Stop()
    {
        _isrunning = false;
    }

    // 打印当前管理的fd
    void   PrintConnection()
    {
        std::cout << "当前 Reactor 管理 fd: ";
        for (auto &conn : _connections)
        {
            std::cout << conn.second->GetSockFd() << " ";
        }
        std::cout << "\r\n";
    }

    ~Reactor()
    {
    }

private:
    std::unique_ptr<Epoller> _epoller_ptr;                             // epoll 封装
    bool _isrunning;                                                   // 运行状态
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // 连接管理
    struct epoll_event _revs[revs_num];                                // 就绪事件数组
};

5)Channel.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "Connection.hpp"

using namespace LogModule;

#define SIZE 1024

// Channel: 客户端连接通道
// 负责:非阻塞读写 + 缓冲区管理 + 事件处理
class Channel : public Connection
{
public:
    Channel(int sockfd, const InetAddr &client)
        : _sockfd(sockfd),
          _client_addr(client)
    {
        SetNonBlock(_sockfd); // ET模式必须非阻塞
    }

    // 读事件处理
    void Recver() override
    {
        char buffer[SIZE];
        while (true)
        {
            ssize_t n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                _inbuffer += buffer; // 数据放入读缓冲区
            }
            else if (n == 0)
            {
                Excepter(); // 对端关闭
                return;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 数据读完
                else if (errno == EINTR)
                    continue; // 被信号中断,重试
                else
                {
                    Excepter(); // 真正出错
                    return;
                }
            }
        }

        LOG(LogLevel::DEBUG) << "Channel: Inbuffer:\n"
                             << _inbuffer;

        // 调用业务回调, 生成响应
        if (!_inbuffer.empty())
        {
            _outbuffer += _handler(_inbuffer);
        }

        // 有数据要发送,主动触发发送
        if (!_outbuffer.empty())
        {
            Sender();
        }
    }

    // 写事件处理
    void Sender() override
    {
        while (true)
        {
            ssize_t n = send(_sockfd, _outbuffer.c_str(), _outbuffer.size(), 0);
            if (n > 0)
            {
                _outbuffer.erase(0, n); // 移除已发送数据
                if (_outbuffer.empty())
                    break;
            }
            else if (n == 0)
                break;
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break; // 发送缓冲区满,下次再发
                if (errno == EINTR)
                    continue;
                else
                {
                    Excepter();
                    return;
                }
            }
        }

        // 根据缓冲区是否为空,动态开关写事件
        if (!_outbuffer.empty())
            GetOwner()->EnableReadWrite(_sockfd, true, true); // 监听写
        else
            GetOwner()->EnableReadWrite(_sockfd, true, false); // 关闭写
    }

    // 异常处理: 关闭连接
    void Excepter() override
    {
        GetOwner()->DelConnection(_sockfd);
    }

    // 获取套接字
    int GetSockFd() override
    {
        return _sockfd;
    }
    
    // 获取读缓冲区
    std::string &Inbuffer()
    {
        return _inbuffer;
    }

    // 追加数据到写缓冲区
    void AppendOutBuffer(const std::string &out)
    {
        _outbuffer += out;
    }

    ~Channel()
    {
    }

private:
    int _sockfd;
    std::string _inbuffer;  // 读缓冲区
    std::string _outbuffer; // 写缓冲区
    InetAddr _client_addr;  // 客户端地址
};

6)Listener.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <memory>
#include "Common.hpp"
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Connection.hpp"
#include "Channel.hpp"

using namespace SocketModule;

// 监听接收器: 专门处理新连接
class Listener : public Connection
{
public:
    Listener(int port = defaultport)
        : _port(port),
          _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildTcpSocketMethod(_port); // 创建监听套接字
        SetEvent(EPOLLIN | EPOLLET);              // ET模式监听读事件
        SetNonBlock(_listensock->Fd());           // 设置非阻塞
    }

    ~Listener()
    {
    }

    // 读事件:处理新连接
    void Recver() override
    {
        InetAddr client;
        while (true)
        {
            std::shared_ptr<Socket> newsock = _listensock->Accept(&client);
            if (!newsock)
                return;

            int sockfd = newsock->Fd();
            if (sockfd == ACCEPT_ERR)
                break;
            if (sockfd == ACCEPT_CONTINUE)
                continue;
            if (sockfd == ACCEPT_DONE)
                break;

            // 建立新连接, 封装成 Channel
            std::shared_ptr<Connection> conn = std::make_shared<Channel>(sockfd, client);
            conn->SetEvent(EPOLLIN | EPOLLET);
            if (_handler != nullptr)
                conn->RegisterHandler(_handler);

            // 加入 Reactor 管理
            GetOwner()->AddConnection(conn);
        }
    }

    // 监听套接字不需要写事件
    void Sender() override
    {
    }

    // 监听套接字异常处理
    void Excepter() override
    {
    }

    // 获取监听 fd
    int GetSockFd() override
    {
        return _listensock->Fd();
    }

private:
    int _port;                           // 监听端口
    std::unique_ptr<Socket> _listensock; // 监听套接字
};

3、协议与业务模块

1)Protocol.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <jsoncpp/json/json.h>
#include "Socket.hpp"

using namespace SocketModule;

// 请求类: 客户端 → 服务端
class Request
{
public:
    Request() {}

    Request(int x, int y, char oper)
        : _x(x), _y(y), _oper(oper)
    {
    }

    // 序列化:对象 → JSON
    std::string Serialize()
    {
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;
        Json::FastWriter writer;
        return writer.write(root);
    }

    // 反序列化:JSON → 对象
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        if (!reader.parse(in, root))
            return false;

        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _oper = root["oper"].asInt();
        return true;
    }

    int X() { return _x; }
    int Y() { return _y; }
    char Oper() { return _oper; }

    ~Request() {}

private:
    int _x;
    int _y;
    char _oper;
};

//  响应类: 服务端 → 客户端
class Response
{
public:
    Response() {}

    Response(int result, int code)
        : _result(result), _code(code)
    {
    }

    // 序列化:对象 → JSON
    std::string Serialize()
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        return writer.write(root);
    }

    // 反序列化:JSON → 对象
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        if (!reader.parse(in, root))
            return false;

        _result = root["result"].asInt();
        _code = root["code"].asInt();
        return true;
    }

    void SetResult(int res) { _result = res; }
    int GetResult() { return _result; }
    void SetCode(int code) { _code = code; }
    int GetCode() { return _code; }

    void ShowResult()
    {
        std::cout << "计算结果是: " << _result << "[" << _code << "]" << std::endl;
    }

    ~Response() {}

private:
    int _result;
    int _code; // 状态码
};

const std::string sep = "\r\n";

// 业务处理函数类型
using func_t = std::function<Response(Request &req)>;

// 协议处理: 粘包解决 + 编解码 + 收发报文
class Protocol
{
public:
    Protocol() {}
    Protocol(func_t func) : _func(func) {}

    // 编码: 长度\r\n数据\r\n
    std::string Encode(const std::string &jsonstr)
    {
        return std::to_string(jsonstr.size()) + sep + jsonstr + sep;
    }

    // 解码: 从缓冲区提取一个完整包
    bool Decode(std::string &buffer, std::string *package)
    {
        ssize_t pos = buffer.find(sep); // 2
        if (pos == std::string::npos)
            return false;

        int len = std::stoi(buffer.substr(0, pos)); // 50
        int total = len + pos + 2 * sep.size();
        if (buffer.size() < total)
            return false;

        *package = buffer.substr(pos + sep.size(), len);
        buffer.erase(0, total);
        return true;
    }

    // 服务端: 处理客户端请求
    void GetRequest(std::shared_ptr<Socket> &sock, InetAddr &client)
    {
        std::string buffer;
        while (true)
        {
            // 1. 读取客户端数据
            int n = sock->Recv(&buffer);
            if (n > 0)
            {
                // 2. 循环解析完整报文
                std::string package;
                while (Decode(buffer, &package))
                {
                    // 3. 反序列化请求
                    LOG(LogLevel::DEBUG) << client.StringAddr() << " 请求: " << package;
                    Request req;
                    if (!req.Deserialize(package))
                        continue;

                    // 4. 执行业务逻辑
                    Response resp = _func(req);

                    // 5. 响应序列化 + 编码发送
                    sock->Send(Encode(resp.Serialize()));
                }
            }
            else if (n == 0) // 客户端关闭连接
            {
                LOG(LogLevel::INFO) << "client: " << client.StringAddr() << " 退出";
                break;
            }
            else // 读取错误
            {
                LOG(LogLevel::WARNING) << "client: " << client.StringAddr() << " 读取异常";
                break;
            }
        }
    }

    // 客户端: 获取服务端响应
    bool GetResponse(std::shared_ptr<Socket> &client, std::string &resp_buff, Response *resp)
    {
        while (true)
        {
            // 1. 读取服务端响应
            int n = client->Recv(&resp_buff);
            if (n > 0)
            {
                // 2. 循环解析完整报文
                std::string package;
                while (Decode(resp_buff, &package))
                {
                    // 3. 反序列化响应
                    resp->Deserialize(package);
                }
                return true;
            }
            else if (n == 0) // 服务端关闭连接
            {
                std::cout << "server quit" << std::endl;
                return false;
            }
            else // 读取错误
            {
                std::cout << "recv error" << std::endl;
                return false;
            }
        }
    }

    // 客户端: 构建请求报文
    std::string BuildRequestString(int x, int y, char oper)
    {
        Request req(x, y, oper);
        std::string json = req.Serialize();
        return Encode(json);
    }
	
	 // 处理单个请求包
    std::string Execute(std::string &package)
    {
        LOG(LogLevel::DEBUG) << "进入到protocol Execute begnin";

        // 反序列化包
        Request req;
        if(!req.Deserialize(package))
            return "";

        // 执行业务计算
        Response resp = _func(req);

        // 序列化并编码(带长度头)
        std::string send_str = Encode(resp.Serialize());

        LOG(LogLevel::DEBUG) << "进入到protocol Execute end";
        return send_str;
    }

    ~Protocol() {}

private:
    func_t _func;
};

2)NetCal.hpp

cpp 复制代码
#pragma once

#include "Protocol.hpp"
#include <iostream>

// 计算器业务类
// 状态码:0成功 1除零 2模零 3非法运算符
class Cal
{
public:
    // 执行计算
    Response Execute(Request &req)
    {
        // 初始化响应: 结果默认0, 状态码默认0
        Response resp(0, 0);
        switch (req.Oper())
        {
        case '+':
            resp.SetResult(req.X() + req.Y());
            break;
        case '-':
            resp.SetResult(req.X() - req.Y());
            break;
        case '*':
            resp.SetResult(req.X() * req.Y());
            break;
        case '/':
            if (req.Y() == 0)
                resp.SetCode(1);
            else
                resp.SetResult(req.X() / req.Y());
            break;
        case '%':
            if (req.Y() == 0)
                resp.SetCode(2);
            else
                resp.SetResult(req.X() % req.Y());
            break;
        default:
            resp.SetCode(3);
            break;
        }
        // 返回包含结果和状态码的响应对象
        return resp;
    }
};

3)Main.cc

cpp 复制代码
#include <iostream>
#include <string>
#include "Log.hpp"
#include "Common.hpp"
#include "Reactor.hpp"
#include "Channel.hpp"
#include "Listener.hpp"
#include "Protocol.hpp"
#include "NetCal.hpp"

static void Usage(std::string proc)
{
    std::cerr << "Usage: " << proc << " port\n";
}

// ./reactorserver port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    Enable_Console_Log_Strategy();
    uint16_t port = std::stoi(argv[1]);

    // 计算器业务
    std::shared_ptr<Cal> cal = std::make_shared<Cal>();

    // 协议模块
    std::shared_ptr<Protocol> protocol = std::make_shared<Protocol>([&](Request &req)
                                                                    { return cal->Execute(req); });

    // 监听连接
    std::shared_ptr<Connection> listener = std::make_shared<Listener>(port);
    listener->RegisterHandler([&](std::string &inbuffer) -> std::string
                              {
        LOG(LogLevel::DEBUG) << "业务回调开始"; 
        std::string response_str;
        std::string package;

        // 循环解析并处理包
        while(true)
        {
            if(!protocol->Decode(inbuffer,&package))
            break;
            response_str += protocol->Execute(package);
        }

        LOG(LogLevel::DEBUG)<<"业务回调结束, 返回: \n"<<response_str;
        return response_str; });

    // 启动 Reactor 服务器
    std::unique_ptr<Reactor> reactor = std::make_unique<Reactor>();
    reactor->AddConnection(listener);
    reactor->Loop();

    return 0;
}

4)Client.cc

cpp 复制代码
#include <iostream>
#include <string>
#include <memory>
#include "Common.hpp"
#include "Socket.hpp"
#include "Protocol.hpp"

using namespace SocketModule;

void Usage(std::string proc)
{
    std::cerr << "Usage: " << proc << " server_ip server_port" << std::endl;
}

// 从控制台读取数据
void GetDaTaFromStdin(int *x, int *y, char *oper)
{
    std::cout << "Please Enter x: ";
    std::cin >> *x;
    std::cout << "Please Enter y: ";
    std::cin >> *y;
    std::cout << "Please Enter oper: ";
    std::cin >> *oper;
}

// ./client server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    // 1. 创建客户端socket
    std::shared_ptr<Socket> client = std::make_shared<TcpSocket>();
    client->BuildTcpClientSocketMethod();

    // 2. 连接服务器
    if (client->Connect(server_ip, server_port) != 0)
    {
        std::cerr << "connect error" << std::endl;
        exit(CONNECT_ERR);
    }

    // 3. 协议处理
    std::unique_ptr<Protocol> protocol = std::make_unique<Protocol>();
    std::string resp_buffer;

    // 4. 循环交互
    while (true)
    {
        int x, y;
        char oper;
        GetDaTaFromStdin(&x, &y, &oper);

        // 构建并发送请求
        std::string req_str = protocol->BuildRequestString(x, y, oper);
        client->Send(req_str);

        // 接收并解析响应
        Response resp;
        if (!protocol->GetResponse(client, resp_buffer, &resp))
            break;

        resp.ShowResult();
    }

    // 5. 关闭连接
    client->Close();

    return 0;
}

客户端

服务端

相关推荐
Kiyra1 小时前
【无标题】
网络
jiayong231 小时前
Memory 写入、检索与纠错机制:让 Agent 记住,也让它忘对
java·服务器·网络·hermes
小赵不会秃头1 小时前
数据结构Day 06:线性结构、库操作及 Makefile 完整学习笔记
java·linux·数据结构·算法·面试
雨田大大1 小时前
Windows11下IDEA运行后端时,端口被占用的解决方法
linux·运维·服务器
IKun-bug1 小时前
CentOS 7 安装 Claude Code 指南
linux·运维·centos
念越1 小时前
HTTPS 安全内核:对称与非对称加密的博弈,数字证书一战定局
java·网络·网络协议·安全·https
kdxiaojie1 小时前
U-Boot分析【学习笔记】(8)
linux·笔记·学习
风曦Kisaki1 小时前
# Linux运维Day02:LNMP架构部署、动静分离原理、Nginx地址重写、systemd服务管理
linux·运维·架构
Shadow(⊙o⊙)1 小时前
Linux进程地址空间——钻入Linux内核架构性剖析 硬核手搓!
java·linux·运维·服务器·开发语言·c++