三种模型对比和reactor模式

1. select 模型

复制代码
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
#include <cstring>
#include <iostream>

class SelectServer {
private:
    int max_fd = 0;
    fd_set read_fds;
    fd_set all_fds;
    std::vector<int> client_fds;
    
public:
    void run(int server_fd) {
        FD_ZERO(&all_fds);
        FD_SET(server_fd, &all_fds);
        max_fd = server_fd;
        
        while (true) {
            // 每次都要重新复制fd_set
            memcpy(&read_fds, &all_fds, sizeof(fd_set));
            
            // 阻塞等待事件
            int ready = select(max_fd + 1, &read_fds, nullptr, nullptr, nullptr);
            
            if (ready < 0) {
                perror("select error");
                break;
            }
            
            // 检查监听socket是否有新连接
            if (FD_ISSET(server_fd, &read_fds)) {
                int client_fd = accept(server_fd, nullptr, nullptr);
                FD_SET(client_fd, &all_fds);
                client_fds.push_back(client_fd);
                max_fd = std::max(max_fd, client_fd);
                std::cout << "New client: " << client_fd << std::endl;
            }
            
            // 检查客户端socket是否有数据
            for (size_t i = 0; i < client_fds.size(); i++) {
                int client_fd = client_fds[i];
                if (FD_ISSET(client_fd, &read_fds)) {
                    char buffer[1024];
                    ssize_t n = recv(client_fd, buffer, sizeof(buffer), 0);
                    if (n <= 0) {
                        close(client_fd);
                        FD_CLR(client_fd, &all_fds);
                        client_fds.erase(client_fds.begin() + i);
                        i--;
                    } else {
                        // 处理数据
                        handleData(client_fd, buffer, n);
                    }
                }
            }
        }
    }
    
private:
    void handleData(int fd, const char* data, size_t len) {
        // 处理业务逻辑
        send(fd, data, len, 0);
    }
};

select的问题

  1. 需要遍历所有fd检查状态

  2. fd_set大小有限制(通常1024)

  3. 每次调用需要复制fd_set

2. poll 模型

复制代码
#include <poll.h>
#include <vector>
#include <iostream>

class PollServer {
private:
    std::vector<pollfd> fds;
    
public:
    void run(int server_fd) {
        // 添加监听socket
        fds.push_back({server_fd, POLLIN, 0});
        
        while (true) {
            // 阻塞等待事件
            int ready = poll(fds.data(), fds.size(), -1);
            
            if (ready < 0) {
                perror("poll error");
                break;
            }
            
            // 遍历所有fd
            for (size_t i = 0; i < fds.size(); i++) {
                if (fds[i].revents & POLLIN) {
                    if (fds[i].fd == server_fd) {
                        // 新连接
                        int client_fd = accept(server_fd, nullptr, nullptr);
                        fds.push_back({client_fd, POLLIN, 0});
                        std::cout << "New client: " << client_fd << std::endl;
                    } else {
                        // 客户端数据
                        char buffer[1024];
                        ssize_t n = recv(fds[i].fd, buffer, sizeof(buffer), 0);
                        if (n <= 0) {
                            close(fds[i].fd);
                            fds.erase(fds.begin() + i);
                            i--;
                        } else {
                            handleData(fds[i].fd, buffer, n);
                        }
                    }
                }
            }
        }
    }
    
private:
    void handleData(int fd, const char* data, size_t len) {
        send(fd, data, len, 0);
    }
};

poll的改进

  1. 没有文件描述符数量限制

  2. 使用数组而不是位图

  3. 但依然需要遍历所有fd

3. epoll 模型

复制代码
#include <sys/epoll.h>
#include <vector>
#include <iostream>
#include <unistd.h>

class EpollServer {
private:
    int epoll_fd;
    static const int MAX_EVENTS = 64;
    
public:
    EpollServer() {
        epoll_fd = epoll_create1(0);
        if (epoll_fd < 0) {
            throw std::runtime_error("epoll_create failed");
        }
    }
    
    ~EpollServer() {
        close(epoll_fd);
    }
    
    void run(int server_fd) {
        // 添加监听socket到epoll
        epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = server_fd;
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
        
        while (true) {
            epoll_event events[MAX_EVENTS];
            
            // 阻塞等待事件,只返回有事件的fd
            int ready = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
            
            if (ready < 0) {
                perror("epoll_wait error");
                break;
            }
            
            // 只处理有事件的fd
            for (int i = 0; i < ready; i++) {
                if (events[i].data.fd == server_fd) {
                    // 接受新连接
                    int client_fd = accept(server_fd, nullptr, nullptr);
                    
                    // 设置为非阻塞模式
                    int flags = fcntl(client_fd, F_GETFL, 0);
                    fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
                    
                    // 添加到epoll
                    epoll_event client_ev;
                    client_ev.events = EPOLLIN | EPOLLET;  // 边缘触发模式
                    client_ev.data.fd = client_fd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_ev);
                    
                    std::cout << "New client: " << client_fd << std::endl;
                } else {
                    // 处理客户端数据
                    handleClient(events[i].data.fd);
                }
            }
        }
    }
    
private:
    void handleClient(int client_fd) {
        char buffer[1024];
        while (true) {  // 边缘触发需要循环读取
            ssize_t n = recv(client_fd, buffer, sizeof(buffer), 0);
            if (n > 0) {
                // 处理数据
                send(client_fd, buffer, n, 0);
            } else if (n == 0) {
                // 连接关闭
                close(client_fd);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
                break;
            } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 数据读取完毕
                break;
            } else {
                perror("recv error");
                close(client_fd);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
                break;
            }
        }
    }
};

epoll的优势

  1. 只返回有事件的fd,无需扫描所有fd

  2. 支持边缘触发(ET)和水平触发(LT)

  3. 没有fd数量限制

  4. 内存共享,无需内核用户空间拷贝

4. Reactor 模式实现

复制代码
#include <functional>
#include <unordered_map>
#include <memory>
#include <sys/epoll.h>
#include <iostream>

// 事件类型
enum EventType {
    READ_EVENT = 1,
    WRITE_EVENT = 2,
    ERROR_EVENT = 4
};

// 事件处理器接口
class EventHandler {
public:
    virtual ~EventHandler() = default;
    virtual void handleEvent(int fd, EventType eventType) = 0;
    virtual int getHandle() const = 0;
};

// Reactor核心类
class Reactor {
private:
    int epoll_fd;
    bool running = false;
    std::unordered_map<int, std::shared_ptr<EventHandler>> handlers;
    
public:
    Reactor() {
        epoll_fd = epoll_create1(0);
    }
    
    ~Reactor() {
        close(epoll_fd);
    }
    
    // 注册事件处理器
    void registerHandler(std::shared_ptr<EventHandler> handler, EventType events) {
        int fd = handler->getHandle();
        handlers[fd] = handler;
        
        epoll_event ev;
        ev.events = convertEvents(events);
        ev.data.fd = fd;
        
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
    }
    
    // 移除事件处理器
    void removeHandler(int fd) {
        handlers.erase(fd);
        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
    }
    
    // 主循环
    void run() {
        running = true;
        const int MAX_EVENTS = 64;
        epoll_event events[MAX_EVENTS];
        
        while (running) {
            int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
            
            for (int i = 0; i < n; i++) {
                int fd = events[i].data.fd;
                auto it = handlers.find(fd);
                if (it != handlers.end()) {
                    EventType eventType = convertEpollEvents(events[i].events);
                    it->second->handleEvent(fd, eventType);
                }
            }
        }
    }
    
    void stop() {
        running = false;
    }
    
private:
    uint32_t convertEvents(EventType events) {
        uint32_t epoll_events = 0;
        if (events & READ_EVENT) epoll_events |= EPOLLIN;
        if (events & WRITE_EVENT) epoll_events |= EPOLLOUT;
        return epoll_events;
    }
    
    EventType convertEpollEvents(uint32_t epoll_events) {
        int events = 0;
        if (epoll_events & EPOLLIN) events |= READ_EVENT;
        if (epoll_events & EPOLLOUT) events |= WRITE_EVENT;
        if (epoll_events & (EPOLLERR | EPOLLHUP)) events |= ERROR_EVENT;
        return static_cast<EventType>(events);
    }
};

// 具体的事件处理器 - Acceptor
class Acceptor : public EventHandler {
private:
    int server_fd;
    Reactor& reactor;
    std::function<void(int)> onAccept;
    
public:
    Acceptor(int port, Reactor& reactor, std::function<void(int)> callback)
        : reactor(reactor), onAccept(callback) {
        
        // 创建监听socket
        server_fd = socket(AF_INET, SOCK_STREAM, 0);
        // ... bind, listen 代码
        
        // 设置为非阻塞
        fcntl(server_fd, F_SETFL, O_NONBLOCK);
    }
    
    void handleEvent(int fd, EventType eventType) override {
        if (eventType & READ_EVENT) {
            int client_fd = accept(server_fd, nullptr, nullptr);
            if (client_fd > 0) {
                onAccept(client_fd);
            }
        }
    }
    
    int getHandle() const override {
        return server_fd;
    }
};

// 具体的事件处理器 - TcpConnection
class TcpConnection : public EventHandler, public std::enable_shared_from_this<TcpConnection> {
private:
    int client_fd;
    Reactor& reactor;
    std::function<void(const std::string&)> onMessage;
    std::string buffer;
    
public:
    TcpConnection(int fd, Reactor& reactor, std::function<void(const std::string&)> callback)
        : client_fd(fd), reactor(reactor), onMessage(callback) {
        
        fcntl(client_fd, F_SETFL, O_NONBLOCK);
    }
    
    void handleEvent(int fd, EventType eventType) override {
        if (eventType & READ_EVENT) {
            char buf[1024];
            ssize_t n = recv(fd, buf, sizeof(buf), 0);
            if (n > 0) {
                buffer.append(buf, n);
                onMessage(buffer);
                buffer.clear();
            } else if (n == 0) {
                // 连接关闭
                reactor.removeHandler(fd);
                close(fd);
            }
        }
        
        if (eventType & WRITE_EVENT) {
            // 处理写事件
        }
        
        if (eventType & ERROR_EVENT) {
            reactor.removeHandler(fd);
            close(fd);
        }
    }
    
    void send(const std::string& data) {
        ::send(client_fd, data.data(), data.size(), 0);
    }
    
    int getHandle() const override {
        return client_fd;
    }
};

5. 使用Reactor的示例

复制代码
int main() {
    Reactor reactor;
    
    // 创建Acceptor
    auto acceptor = std::make_shared<Acceptor>(8080, reactor, 
        [&reactor](int client_fd) {
            // 新连接回调
            auto connection = std::make_shared<TcpConnection>(client_fd, reactor,
                [](const std::string& msg) {
                    std::cout << "Received: " << msg << std::endl;
                });
            
            // 注册到Reactor
            reactor.registerHandler(connection, READ_EVENT);
        });
    
    // 注册Acceptor
    reactor.registerHandler(acceptor, READ_EVENT);
    
    // 启动Reactor
    reactor.run();
    
    return 0;
}

核心概念总结

特性 select poll epoll Reactor
效率 O(n)扫描 O(n)扫描 O(1)事件通知 事件驱动
连接数 有限制(1024) 无限制 无限制 无限制
内存拷贝 每次调用都要拷贝 需要遍历 内存共享 封装了epoll
触发模式 水平触发 水平触发 水平/边缘触发 可配置
编程复杂度 简单 简单 中等 较高但结构清晰

Reactor模式核心思想

  1. 事件分离:将事件检测和事件处理分离

  2. 事件分发:将事件分发给对应的处理器

  3. 非阻塞I/O:使用非阻塞I/O模型

  4. 单线程/多线程:可以在单线程中处理所有I/O,也可以结合线程池

这种模式广泛应用于Nginx、Redis、Netty等高性能网络框架中。

相关推荐
安科士andxe9 小时前
深入解析|安科士1.25G CWDM SFP光模块核心技术,破解中长距离传输痛点
服务器·网络·5g
寻寻觅觅☆12 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
YJlio12 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
fpcc12 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
偷吃的耗子12 小时前
【CNN算法理解】:三、AlexNet 训练模块(附代码)
深度学习·算法·cnn
l1t12 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
CTRA王大大12 小时前
【网络】FRP实战之frpc全套配置 - fnos飞牛os内网穿透(全网最通俗易懂)
网络
儒雅的晴天13 小时前
大模型幻觉问题
运维·服务器
赶路人儿13 小时前
Jsoniter(java版本)使用介绍
java·开发语言
testpassportcn13 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it