socket epoll 实现
时间: 2023-12-01 10:02:20 浏览: 89
Socket epoll是Linux中的网络I/O模型之一,它是一种高效的I/O多路复用机制,能够同时监听多个文件描述符,当其中任意一个文件描述符发生事件时,就能够通知应用程序进行相应的处理。
实现epoll的基本步骤如下:
1. 创建一个epoll实例,使用epoll_create函数。
2. 向epoll实例中添加监听事件,使用epoll_ctl函数。
3. 调用epoll_wait函数等待事件的发生,如果有事件发生,则返回就绪的文件描述符列表。
4. 根据就绪的文件描述符进行相应的处理。
下面是一个简单的示例代码,实现了一个简单的TCP服务器,使用epoll监听多个连接:
#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define MAX_EVENTS 10
#define BUFFER_SIZE 1024
int setnonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags < 0) {
return -1;
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
return -1;
}
return 0;
}
int main() {
int server_fd, client_fd, epoll_fd, nfds, n, i;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len;
char buffer[BUFFER_SIZE];
struct epoll_event ev, events[MAX_EVENTS];
// 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
exit(1);
}
// 设置socket为非阻塞模式
if (setnonblocking(server_fd) < 0) {
perror("setnonblocking");
exit(1);
}
// 绑定地址和端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(12345);
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
exit(1);
}
// 开始监听
if (listen(server_fd, SOMAXCONN) < 0) {
perror("listen");
exit(1);
}
// 创建epoll实例
epoll_fd = epoll_create1(0);
if (epoll_fd < 0) {
perror("epoll_create1");
exit(1);
}
// 添加server_fd到epoll实例中
ev.events = EPOLLIN;
ev.data.fd = server_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
perror("epoll_ctl");
exit(1);
}
while (1) {
// 等待事件的发生
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds < 0) {
perror("epoll_wait");
exit(1);
}
// 处理就绪的文件描述符
for (i = 0; i < nfds; i++) {
if (events[i].data.fd == server_fd) {
// 有新的连接请求
client_len = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
continue;
}
// 设置client_fd为非阻塞模式
if (setnonblocking(client_fd) < 0) {
perror("setnonblocking");
exit(1);
}
// 添加client_fd到epoll实例中
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) {
perror("epoll_ctl");
exit(1);
}
} else {
// 有数据可读
memset(buffer, 0, BUFFER_SIZE);
while ((n = read(events[i].data.fd, buffer, BUFFER_SIZE)) > 0) {
printf("Received %d bytes: %s\n", n, buffer);
}
if (n < 0 && errno != EAGAIN) {
perror("read");
exit(1);
}
// 关闭连接
if (n == 0) {
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL) < 0) {
perror("epoll_ctl");
exit(1);
}
close(events[i].data.fd);
}
}
}
}
return 0;
}
阅读全文