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的问题:
-
需要遍历所有fd检查状态
-
fd_set大小有限制(通常1024)
-
每次调用需要复制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的改进:
-
没有文件描述符数量限制
-
使用数组而不是位图
-
但依然需要遍历所有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的优势:
-
只返回有事件的fd,无需扫描所有fd
-
支持边缘触发(ET)和水平触发(LT)
-
没有fd数量限制
-
内存共享,无需内核用户空间拷贝
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模式核心思想:
-
事件分离:将事件检测和事件处理分离
-
事件分发:将事件分发给对应的处理器
-
非阻塞I/O:使用非阻塞I/O模型
-
单线程/多线程:可以在单线程中处理所有I/O,也可以结合线程池
这种模式广泛应用于Nginx、Redis、Netty等高性能网络框架中。