libevent简单实现
来源:互联网 发布:chrome 控制台 执行js 编辑:程序博客网 时间:2024/04/28 08:23
对epoll进行的封装
epoll(7)
还记得前段时间去百度面试,被问道select、poll、epoll的区别,机械式的把书本上看到的内容复述了一遍。
面试官:谈谈你自己的理解。
一时语塞,说道,epoll写代码难度有点大。面试官点了点头目测很认可。
这就与epoll的结构体有关了,比如它的void*指针可以只想用户结构体,用户结构提又可以封装一系列内容例如fd、回调等等。
man 2 epola_wait
看看它的结构体里面到底有什么。
typedef union epoll_data{ void * ptr; int fd; uint32_t u32; uint64_t u64;}epoll_data_t;struct epoll_event{ uint32_t events; epoll_data_t data;};
Reactor
Reactor是一个设计模式,它包含了几个部分:
Handle句柄:用来标志描述符
Demultiplexer:事件分发器,即select/poll/epoll
Event Handler : 事件处理接口
Reactor: 用来注册删除事件句柄,运行事件循环,就绪事件时,分发事件到回调函数上处理
libevent简单实现
#include <stdlib.h>#include <stdio.h> #include <stdio.h>#include <sys/socket.h>#include <sys/epoll.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <time.h>#define MAX_EVENTS 1024#define BUFLEN 128#define SERV_PORT 8080/* * status:1表示在监听事件中,0表示不在 * last_active:记录最后一次响应时间,做超时处理 */struct myevent_s { int fd; //cfd listenfd int events; //EPOLLIN EPLLOUT void *arg; //指向自己结构体指针 void (*call_back)(int fd, int events, void *arg); int status; char buf[BUFLEN]; int len; long last_active;};int g_efd; /* epoll_create返回的句柄 */struct myevent_s g_events[MAX_EVENTS+1]; /*+1最后一个用于listenfd*/void eventset(struct myevent_s *ev, int fd, void (*call_back)(int,int,void *),void *arg){ ev->fd = fd; ev->call_back = call_back; ev->events = 0; ev->arg = arg; ev->status = 0; //memset(ev->buf, 0, sizeof(ev->buf)); //ev->len = 0; ev->last_active = time(NULL); return;}void eventset(struct myevent_s *ev,int fd, void (*call_back)(int,int,void*), void*arg){ ev->fd = fd; ev->call_back = call_back; ev->events = 0; ev->arg = arg; ev->status = 0; //memset(ev->buf, 0, sizeof(ev->buf)); //ev->len = 0; ev->last_active = time(NULL); return;}void recvdata(int fd, int events, void *arg);void senddata(int fd, int events, void *arg);void eventadd(int efd, int events, struct myevent_s *ev){ struct epoll_event epv ={0, {0}}; int op; epv.data.ptr = ev; epv.events= ev->events=events; if (ev->status == 1) { op = EPOLL_CTL_MOD; } else { op = EPOLL_CTL_ADD; ev->status = 1; } if (epoll_ctl(efd, op, ev->fd, &epv) < 0) printf("event add failed [fd=%d],events[%d]\n", ev->fd, events); else printf("event add OK [fd=%d], op=%d, events[%0X]\n", ev->fd, op,events); return;}void eventdel(int efd, struct myevent_s *ev){ struct epoll_event epv = {0, {0}}; if (ev->status !=1) return; epv.data.ptr = ev; ev->status = 0; epoll_ctl(efd, EPOLL_CTL_DEL, ev->fd, &epv); return;}void acceptconn(int lfd,int events,void *arg){ struct sockaddr_in cin; socklen_t len = sizeof(cin); int cfd, i; if ((cfd =accept(lfd, (struct sockaddr *)&cin, &len)) == -1) { if (errno != EAGAIN && errno != EINTR) { /* 暂时不做出错处理 */ } printf("%s:accept, %s\n", __func__,strerror(errno)); return; } do { for(i=0;i<MAX_EVENTS;i++) { if (g_events[i].status==0) break; } if(i==MAX_EVENTS) { printf("%s: max connect limit[%d]\n",__func__,MAX_EVENTS); break; } int flag=0; if((flag=fcntl(cfd,F_SETFL,O_NONBLOCK)) <0) { printf("%s: fcntl nonblocking failed, %s\n", __func__,strerror(errno)); break; } eventset(&g_events[i],cfd,recvdata,&g_events[i]); eventadd(g_efd, EPOLLIN,&g_events[i]); }while(0); printf("new connect [%s:%d][time:%ld],pos[%d]\n",inet_ntoa (cin.sin_addr),ntohs(cin.sin_port),g_events[i].last_active,i); return;}void recvdata(int fd,int events, void*arg){ struct myevent_s *ev=(struct myevent_s*)arg; int len; len =recv(fd,ev->buf,sizeof(ev->buf),0); eventdel(g_efd,ev); if (len>0) { ev->len =len; ev->buf[len]='\0'; printf("C[%d]:%s\n",fd, ev->buf); /* 转换为发送事件 */ eventset(ev,fd,senddata,ev); eventadd(g_efd,EPOLLOUT,ev); } else if(len==0) { close(ev->fd); /*ev-g_events地址相减得到偏移元素位置 */ printf("[fd=%d] pos[%d],closed\n",fd,(int)(ev-g_events)); } else { close(ev->fd); printf("recv[fd=%d] error[%d]:%s\n",fd,errno,strerror(errno)); } return;}void senddata(int fd,int events,void *arg){ struct myevent_s *ev=(struct myevent_s*)arg; int len; len=send(fd,ev->buf,ev->len,0); //printf("fd=%d\tev->buf=%s\ttev->len=%d\n", fd, ev->buf, ev->len); //printf("send len = %d\n", len); eventdel(g_efd,ev); if (len>0) { printf("send[fd=%d],[%d]%s\n",fd,len,ev->buf); eventset(ev,fd,recvdata,ev); eventadd(g_efd, EPOLLIN,ev); } else { close(ev->fd); printf("send[fd=%d] error %s\n",fd,strerror(errno)); } return;}void initlistensocket(int efd,short port){ int lfd=socket(AF_INET,SOCK_STREAM,0); fcntl(lfd,F_SETFL,O_NONBLOCK); eventset(&g_events[MAX_EVENTS],lfd,acceptconn,&g_events[MAX_EVENTS]); eventadd(efd,EPOLLIN,&g_events[MAX_EVENTS]); struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family=AF_INET; sin.sin_addr.s_addr=INADDR_ANY; sin.sin_port=htons(port); bind(lfd,(struct sockaddr*)&sin,sizeof(sin)); listen(lfd,20); return;}int main(int argc,char*argv[]){ unsigned short port =SERV_PORT; if (argc==2) port=atoi(argv[1]); g_efd=epoll_create(MAX_EVENTS+1); if (g_efd<=0) printf("create efd in %s err %s\n", __func__,strerror(errno)); initlistensocket(g_efd,port); /* 事件循环 */ struct epoll_event events[MAX_EVENTS+1]; printf("server running:port[%d]\n", port); int checkpos=0,i; while(1){ /* 超时验证,每次测试100个链接,不测试listenfd 当客户端60秒内没 有和服务器通信,则关闭此客户端链接 */ long now=time(NULL); for(i=0;i<100;i++,checkpos++) { if(checkpos ==MAX_EVENTS) checkpos=0; if (g_events[checkpos].status!=1) continue; long duration=now-g_events[checkpos].last_active; if (duration>=60) { close(g_events[checkpos].fd); printf("[fd=%d] timeout\n",g_events[checkpos].fd); eventdel(g_efd, &g_events[checkpos]); } } /* 等待事件发生 */ int nfd =epoll_wait(g_efd,events,MAX_EVENTS+1,1000); if(nfd<0) { printf("epoll_wait error, exit\n"); break; } for(i=0;i<nfd;i++) { struct myevent_s*ev =(struct myevent_s*)events[i].data.ptr; if ((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) { ev->call_back(ev->fd,events[i].events,ev->arg); } if ((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) { ev->call_back(ev->fd,events[i].events,ev->arg); } } } /*退出前释放所有资源*/ return 0;}
参考
- 深入浅出 Libevent 作者:刘丹冰
- https://segmentfault.com/a/1190000002715832 (Reactor 和 Proactor)
- Linux man page
1 0
- libevent简单实现
- libevent实现的简单定时器功能
- libevent学习---简单http server实现
- 使用libevent实现最简单的android http-server
- 使用libevent实现最简单的android http-server
- 利用 libevent 实现简单 http client GET、POST
- 利用 libevent 实现简单 http client GET、POST
- 利用 libevent 实现简单 http client GET、POST
- libevent实现的一个简单的服务器和客户端程序
- libevent简单分析
- libevent简单分析
- libevent的简单应用
- libevent简单介绍
- libevent简单应用_2
- libevent简单应用_4
- libevent简单示例
- libevent的简单应用 .
- libevent简单分析
- Checked异常和Runtime异常
- python之excel处理画图
- setBuddy函数
- 组合模式——公司组织结构
- 委托事件
- libevent简单实现
- JavaFX本地应用自动更新功能的实现——FXLauncher
- 异常跟踪栈
- 异常链
- 557. Reverse Words in a String III\521. Longest Uncommon Subsequence I
- 深入理解RunLoop
- 抽象工厂模式——肯德基消费
- 递归方法的重要规定——递归一定要向己知方向递归
- silly的一次大规模重构