#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define BUF_SIZE 1024
int main(int argc, char *argv[]) {
int listen_sock, conn_sock, epoll_fd, n;
struct sockaddr_in serv_addr, cli_addr;
socklen_t cli_len = sizeof(cli_addr);
struct epoll_event ev, events[MAX_EVENTS];
char buf[BUF_SIZE];
// 创建监听套接字
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 绑定监听地址和端口
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(8888);
if (bind(listen_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(listen_sock, 5) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 创建epoll实例
epoll_fd = epoll_create1(0);
if (epoll_fd < 0) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
// 将监听套接字添加到epoll监视集合中
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_sock, &ev) < 0) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
// 进入主循环,等待事件发生
while (1) {
n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (n < 0) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
for (int i = 0; i < n; i++) {
if (events[i].data.fd == listen_sock) { // 监听套接字有连接请求
conn_sock = accept(listen_sock, (struct sockaddr *)&cli_addr, &cli_len);
if (conn_sock < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("Accepted connection from %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
// 将新连接套接字添加到epoll监视集合中
ev.events = EPOLLIN;
ev.data.fd = conn_sock;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_sock, &ev) < 0) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else { // 客户端套接字有数据可读
int fd = events[i].data.fd;
n = read(fd, buf, BUF_SIZE);
if (n <= 0) {
close(fd);
printf("Connection closed\n");
} else {
write(fd, buf, n);
}
}
}
}
close(listen_sock);
return 0;
}
例中使用epoll机制实现了一个基于事件驱动的并发服务器,它可以同时处理多个客户端连接。
在主循环中,程序调用epoll_wait函数等待事件发生。当有事件发生时,epoll_wait会返回一个包含所有发生事件的结构体数组events,我们遍历此数组并根据每个事件的类型进行相应的处理。
如果事件类型是EPOLLIN,表示对应的文件描述符上有数据可读;程序会读取数据并直接回显给客户端。如果事件类型是EPOLLOUT,表示对应的文件描述符可写;程序会将之前存储的数据发送给客户端。如果事件类型是EPOLLRDHUP或EPOLLHUP,表示对应的文件描述符已经断开连接;程序会关闭该套接字并从epoll监视集合中移除。如果事件类型是EPOLLERR,表示对应的文件描述符出现错误;程序也会关闭该套接字并从epoll监视集合中移除。
如果事件类型是EPOLLIN且对应的文件描述符是监听套接字,则表示有新的客户端连接请求;程序会接受新连接,并将其加入epoll监视集合。
总体来说,该程序通过epoll机制实现了高效的事件驱动并发服务器,避免了传统select和poll机制可能存在的性能问题。
————————————————
版权声明:本文为CSDN博主「逆风水手」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_21688871/article/details/129944924