epoll和reactor(反应堆)模型
epoll的特点和工作方式:
首先epoll是管理网络I/O的,还有select,poll等。他们关注的是网络I/O进而去处理事件。
例如客户端/服务器中,在服务器端即是监听listenfd,接收连接请求后的clientfd
通过epoll监听返回事件集合。并且遍历所有的fd寻找到对应的fd再进行读写事件的判断。
server代码如下:
#include<func.h>
int main(int argc,char* argv[]){
ARGS_CHECK(argc,3);
int listenfd = socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(listenfd,-1,"socket");
int reuse = 1;
int ret = 0;
ret = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
ERROR_CHECK(ret,-1,"setsockopt");
struct sockaddr_in data;
data.sin_family = AF_INET;
data.sin_addr.s_addr = inet_addr(argv[1]);
data.sin_port = htons(atoi(argv[2]));
ret = bind(listenfd,(struct sockaddr*)&data,sizeof(data));
ERROR_CHECK(ret,-1,"bind");
ret = listen(listenfd,10);
ERROR_CHECK(ret,-1,"listen");
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
int addrlen = sizeof(struct sockaddr_in);
int clientfd = accept(listenfd,(struct sockaddr*)&clientaddr,(socklen_t*)&addrlen);
//int clientfd = accept(listenfd,NULL,NULL);
ERROR_CHECK(clientfd,-1,"accept");
int epollfd = epoll_create(1);
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = STDIN_FILENO;
ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
event.events = EPOLLIN;
event.data.fd = listenfd;
ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
event.events = EPOLLIN;
event.data.fd = clientfd;
ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,clientfd,&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
int redyNum = 0;
char buf[128] = {0};
struct epoll_event* events = (struct epoll_event*)calloc(3,sizeof(struct epoll_event));
while(1){
redyNum = epoll_wait(epollfd,events,3,-1);
if(redyNum > 0){
for(int i = 0;i < redyNum;i++){
if(events[i].data.fd == STDIN_FILENO){
memset(buf,0,sizeof(buf));
read(STDIN_FILENO,buf,sizeof(buf));
send(clientfd,&buf,strlen(buf)-1,0);
}else if(events[i].data.fd == clientfd){
memset(buf,0,sizeof(buf));
ret = recv(clientfd,&buf,sizeof(buf),0);
if(ret == 0){
printf("bye bye\n");
close(clientfd);
}else printf("%s\n",buf);
}else if(events[i].data.fd == listenfd){
clientfd = accept(listenfd,(struct sockaddr*)&clientaddr,(socklen_t*)&addrlen);
//clientfd = accept(listenfd,NULL,NULL);
ERROR_CHECK(clientfd,-1,"accept");
printf("clientfd = %d\n",clientfd);
events[i].events = EPOLLIN;
events[i].data.fd = clientfd;
ret = epoll_ctl(epollfd,EPOLL_CTL_ADD,clientfd,&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
}
}
}
}
close(listenfd);
close(clientfd);
return 0;
}
reactor(反应堆)模式:
是基于epoll的一种关注事件的模型。该模式首先要明确关注的事件是什么,注册对应的事件处理器,在事件发生时调用对应的事件处理器。一般通过回调函数来实现。
例如服务器端监听网络I/O时候,其关注就可以是读写事件,将fd与对应的事件处理机制绑定,通过函数指针的方式。epoll_wait返回时就只需要找到有事件发生的fd,调用其注册的回调函数。
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
int accept(int fd,int events,void* arg);
int recvbuf(int fd,int events,void* arg);
int sendbuf(int fd,int events,void* arg);
struct sockiteam{
int sockfd;
int(*callback)(int fd,int events,void* arg);
char recvbuff[1024];
char sendbuff[1024];
};
struct reactor{
int epfd;
struct epoll_event events[512];
};
struct reactor* eventloop = NULL;
int accept(int fd,int events,void* arg)
{
struct sockiteam* is = (struct sockiteam*)arg;
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
socklen_t len = sizeof(struct sockaddr_in);
int listenfd = accept(is->sockfd,(struct sockaddr*)&addr,&len);
if(listenfd < 0) return -1;
struct sockiteam* si = (struct sockiteam*)calloc(1,sizeof(struct sockiteam));
si->sockfd = listenfd;
si->callback = recvbuf;
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listenfd;
ev.data.ptr = si;
if(epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,si->sockfd,&ev) < 0) return -1;
printf("new connect is sucess!\n");
return 0;
}
int recvbuf(int fd,int events,void* arg)
{
struct sockiteam* is = (struct sockiteam*)arg;
memset(&is->recvbuff,0,sizeof(is->recvbuff));
int ret = recv(is->sockfd,is->recvbuff,1024,0);
if(ret < 0){
if(errno == EAGAIN || errno == EWOULDBLOCK){
return -1;
}else{
}
//姝ゆ椂杩炴帴鍑洪敊锛岄渶瑕佺Щ闄ゅ叧娉ㄧ殑浜嬩欢
struct epoll_event ev;
ev.events = EPOLLIN;
epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,is->sockfd,&ev);
close(fd);
free(is);
}else if(ret == 0){
printf("disconnect!\n");
struct epoll_event ev;
ev.events = EPOLLIN;
epoll_ctl(eventloop->epfd,EPOLL_CTL_DEL,is->sockfd,&ev);
close(fd);
free(is);
}else{
printf("Recv %d Bytes from client is : %s\n",ret,is->recvbuff);
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
memset(&is->sendbuff,0,sizeof(is->sendbuff));
strcpy(is->sendbuff,is->recvbuff);
ev.events = EPOLLOUT | EPOLLET;
is->callback = sendbuf;
ev.data.ptr = is;
epoll_ctl(eventloop->epfd,EPOLL_CTL_MOD,is->sockfd,&ev);
}
return 0;
}
int sendbuf(int fd,int events,void* arg)
{
struct sockiteam* is = (struct sockiteam*)arg;
int ret = send(is->sockfd,is->sendbuff,strlen(is->sendbuff),0);
if(ret < 0){
}else{
}
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLIN | EPOLLET;
ev.data.ptr = is;
is->callback = recvbuf;
if(epoll_ctl(eventloop->epfd,EPOLL_CTL_MOD,is->sockfd,&ev) < 0) return -1;
return 0;
}
int main(int argc,char** argv){
if(argc < 3) return -1;
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0) return -1;
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr)) < 0)
return -1;
if(listen(sockfd,5) < 0) return -1;
eventloop = (struct reactor*)calloc(1,sizeof(struct reactor));
eventloop->epfd = epoll_create(1);
struct sockiteam* is = (struct sockiteam*)calloc(1,sizeof(struct sockiteam));
is->callback = accept;
is->sockfd = sockfd;
struct epoll_event ev;
memset(&ev,0,sizeof(struct epoll_event));
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = is->sockfd;
ev.data.ptr = is;
epoll_ctl(eventloop->epfd,EPOLL_CTL_ADD,is->sockfd,&ev);
while(1){
int nready = epoll_wait(eventloop->epfd,eventloop->events,512,-1);
for(int i = 0;i < nready;++i){
if(eventloop->events[i].events & EPOLLIN){
struct sockiteam* si = (struct sockiteam*)eventloop->events[i].data.ptr;
si->callback(si->sockfd,eventloop->events[i].events,si);
}
if(eventloop->events[i].events & EPOLLOUT){
struct sockiteam* si = (struct sockiteam*)eventloop->events[i].data.ptr;
si->callback(si->sockfd,eventloop->events[i].events,si);
}
}
}
}