当前位置:网站首页>epoll 的EPOLLONESHOT 事件———实例程序
epoll 的EPOLLONESHOT 事件———实例程序
2022-04-23 14:36:00 【m0_51551385】
本实例程序是一个服务器,他接收用户数据,并输出到标准输出,他的特点是:对于每一个EPOLLIN事件,都唤醒一个线程去处理他,而主进程继续往下执行,也就是进入下一轮循环,调用epoll_wait监听其他事件。
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<assert.h>
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include<pthread.h>
#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 1024
struct fds
{
int epollfd;
int sockfd;
};
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
/* 将 fd 上的 EPOLLIN 和 EPOLLET 事件注册到 epollfd 指示的 epoll 内核事件表中,参数 oneshot 指定是否注册 fd 上的 EPOLLONESHOT 事件 */
void addfd(int epollfd, int fd, bool oneshot)
{
epoll_event event;
event.events = EPOLLIN | EPOLLET;
if (oneshot)
{
event.events |= EPOLLONESHOT;
}
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
}
/* 重置 fd 上的事件。这样操作之后,尽管 fd 上的 EPOLLONESHOT 事件被注册,但是操作系统仍然会触发 fd 上的 EPOLLIN 事件,且只触发一次 */
void reset_oneshot(int epollfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}
/* 工作线程 */
void* worker(void* arg)
{
int sockfd = ((fds*)arg)->sockfd;
int epollfd = ((fds*)arg)->epollfd;
printf("start new thread to receive data on fd %d\n", sockfd);
char buf[BUFFER_SIZE];
memset(buf, '\0', BUFFER_SIZE);
/* 循环读取 sockfd 上的数据,指导遇到 EAGAIN 错误 */
while (1)
{
int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
if (ret == 0)
{
close(sockfd);
printf("foreiner closed the connection\n");
break;
}
else if (ret < 0)
{
if (errno == EAGAIN)
{
reset_oneshot(epollfd, sockfd);
printf("read later\n");
break;
}
}
else
{
printf("get content: %s\n", buf);
/* 休眠 5s,模拟数据处理过程 */
sleep(5);
}
}
printf("end thread receiving data on fd: %d\n", sockfd);
}
int main(int argc, char* argv[])
{
if (argc <= 2)
{
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address.sin_addr);
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(10);
assert(epollfd != -1);
/* 注意,监听sockfd listenfd 上不能注册EPOLLONESHOT事件,否则由于程序就只能处理一个客户端连接!因为后续客户的连接请求不再触发 listenfd 上的 EPOLLIN 事件*/
addfd(epollfd, listenfd, false);
while (1)
{
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if (ret < 0)
{
printf("epoll failure\n");
break;
}
for (int i = 0; i < ret; i++)
{
int sockfd = events[i].data.fd;
if (sockfd == listenfd)
{
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
/* 对每个非监听文件描述符都注册 EPOLLONESHOT 事件 */
addfd(epollfd, connfd, true);
}
else if (events[i].events & EPOLLIN) /* 对每个 EPOLLIN 事件开启一个线程,然后进程继续往下执行下一次 epoll_wait */
{
pthread_t thread;
fds fds_for_new_worker;
fds_for_new_worker.epollfd = epollfd;
fds_for_new_worker.sockfd = sockfd;
/* 新启动一个工作线程为 sockfd 服务 */
pthread_create(&thread, NULL, worker, (void*)&fds_for_new_worker);
}
else
{
printf("something else happened\n");
}
}
}
close(listenfd);
return 0;
}
版权声明
本文为[m0_51551385]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_51551385/article/details/124359780
边栏推荐
- 555定时器+74系列芯片搭建八路抢答器,30s倒计时,附Proteus仿真等
- 555 timer + 74 series chip to build eight way responder, 30s countdown, proteus simulation, etc
- 一篇博客让你学会在vscode上编写markdown
- 利用 MATLAB 编程实现最速下降法求解无约束最优化问题
- Upgrade of openssh and modification of version number
- 顺序表的操作,你真的学会了吗?
- ArrayList集合基本使用
- Swift Protocol 关联对象 资源名称管理 多线程GCD 延迟 once
- Electronic perpetual calendar of DS1302_ 51 single chip microcomputer, month, day, week, hour, minute and second, lunar calendar and temperature, with alarm clock and complete set of data
- Detailed explanation of C language P2 selection branch statement
猜你喜欢

【无标题】

ArrayList collection basic usage

TLS/SSL 协议详解 (28) TLS 1.0、TLS 1.1、TLS 1.2之间的区别

SHT11传感器的温度湿度监控报警系统单片机Proteus设计(附仿真+论文+程序等)

想要成为架构师?夯实基础最重要

51 MCU flowers, farmland automatic irrigation system development, proteus simulation, schematic diagram and C code

Detailed explanation of SAR command

C语言知识点精细详解——数据类型和变量【1】——进位计数制

面试官:说一下类加载的过程以及类加载的机制(双亲委派机制)

外包干了四年,废了...
随机推荐
TLS/SSL 协议详解 (28) TLS 1.0、TLS 1.1、TLS 1.2之间的区别
API Gateway/API 网关(三) - Kong的使用 - 限流rate limiting(redis)
Proteus simulation design of DC adjustable regulated power supply (with simulation + paper and other data)
C语言知识点精细详解——数据类型和变量【2】——整型变量与常量【1】
Qt界面优化:Qt去边框与窗体圆角化
do(Local scope)、初始化器、内存冲突、Swift指针、inout、unsafepointer、unsafeBitCast、successor、
C语言知识点精细详解——初识C语言【1】——你不能不知的VS2022调试技巧及代码实操【2】
直流可调稳压电源的Proteus仿真设计(附仿真+论文等资料)
AT89C52 MCU frequency meter (1Hz ~ 20MHz) design, LCD1602 display, including simulation, schematic diagram, PCB and code, etc
51 Single Chip Microcomputer Design of traffic light system (with Proteus simulation, C program, schematic diagram, PCB, thesis and other complete data)
1 minute to understand the execution process and permanently master the for cycle (with for cycle cases)
The initial C language framework is suitable for review and preliminary understanding
kprobe 的 3 种使用
Solve the problem of SSH configuration file optimization and slow connection
Swift Protocol 关联对象 资源名称管理 多线程GCD 延迟 once
【工厂模式详解】工厂方法模式
Sed learning for application
raised exception class EAccexxViolation with ‘Access violation at address 45EFD5 in module 出错
四层和八层电梯控制系统Proteus仿真设计,51单片机,附仿真和Keil C代码
Swift:Entry of program、Swift调用OC、@_silgen_name 、 OC 调用Swift、dynamic、String、Substring