在使用 Reactor 模式处理新来的连接时,通常会遵循以下步骤。Reactor 模式主要用于事件驱动的网络编程,它通过分离事件处理和事件循环来实现高效的并发连接管理。
Reactor 模式处理新连接的步骤
- 创建监听套接字: 首先,创建一个监听套接字并绑定到指定端口。将该套接字设置为非阻塞模式,以避免在接受新连接时造成阻塞。
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(listen_fd, F_SETFL, O_NONBLOCK); // 设置非阻塞模式
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(listen_fd, SOMAXCONN); // 开始监听
创建 I/O 多路复用器: 创建一个 I/O 多路复用器(如
epoll
或select
),用于监控监听套接字和后续的客户端连接。注册监听事件: 将监听套接字注册到 I/O 多路复用器中,并指定感兴趣的事件(通常是可读事件)。
进入事件循环: 进入主事件循环,等待可读事件发生。
处理新来的连接: 一旦检测到有新的可读事件(即有新的客户端连接请求),执行以下步骤:
- 使用
accept()
接受新连接。 - 将新建立的连接设置为非阻塞模式。
- 注册新连接到 I/O 多路复用器中,以便能监测其后续的数据传输。
- 使用
示例代码
下面是一个简单的示例,演示了如何在 Reactor 模式下处理新来的 TCP 连接:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define MAX_EVENTS 1024
#define PORT 8080
void set_nonblocking(int sockfd) {
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
}
int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
set_nonblocking(listen_fd); // 设置为非阻塞
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
listen(listen_fd, SOMAXCONN); // 开始监听
// 创建 epoll 实例
int epfd = epoll_create1(0);
struct epoll_event ev;
ev.events = EPOLLIN; // 可读事件
ev.data.fd = listen_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev); // 注册监听 fd 到 epoll
while (1) {
struct epoll_event events[MAX_EVENTS];
int nready = epoll_wait(epfd, events, MAX_EVENTS, -1); // 阻塞等待
for (int i = 0; i < nready; ++i) {
if (events[i].data.fd == listen_fd) { // 检测是否为监听 fd
while (1) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int conn_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &addr_len);
if (conn_fd < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
break; // 没有更多可以接受的连接
perror("accept");
break;
}
set_nonblocking(conn_fd); // 新链接设为非阻塞
// 将新链接注册到 epoll 中以监视可读事件
ev.events = EPOLLIN | EPOLLET; // 边缘触发
ev.data.fd = conn_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd , &ev);
}
} else {
// 此处可以添加对已建立客户端链接数据读取逻辑的处理...
}
}
}
close(listen_fd);
}
注意事项
异常处理: 确保在调用系统调用时进行错误检查,如
socket()
,bind()
,listen()
, 和accept()
等函数。资源管理: 不要忘记关闭不再使用的文件描述符,以避免资源泄漏。
数据读取与写入: 当新连接被接受后,需要适当设计逻辑来读取和写入数据,同时考虑选择边缘触发(EPOLLET)或水平触发(EPOLLET)的工作方式,根据实际需求做出决定。
Reactor 模式非常适合于高性能网络服务,可以有效地管理大量并发网络连接。
内容由零声教学AI助手提供,问题来源于学员提问