当前位置: 代码迷 >> 综合 >> poll-epoll服务器
  详细解决方案

poll-epoll服务器

热度:87   发布时间:2023-12-12 23:54:07.0

poll

相对于select来说,poll的优点:
1)能监视的文件描述符的数量没有限制
2)poll输入和输出参数不是同一变量,输入和输出分离
缺点:
1)和select一样,监视的文件描述符过多时,后序也许需要进行遍历式访问,性能也会下降。(为了解决这个问题所以用到了epoll)
makefile

mypoll:mypoll.cgcc -o $@ $^
.PHONY:clean
clean:rm -f mypoll

mypoll

#include <stdio.h>
#include <poll.h>int main()
{struct pollfd ev;ev.fd = 0;ev.events = POLLIN;ev.revents = 0;int timeout = -1;while(1){switch(poll(&ev,1,timeout)){case 0:sleep(1);printf("timeout...\n");break;case -1:perror("poll");break;default:{char buf[1024];if(ev.fd == 0&& ev.revents & POLLIN){ssize_t s = read(0,buf,sizeof(buf)-1);if(s>0){buf[s-1] = 0;printf("echo %s\n",buf);}}}break;}}return 0;
}

epoll

linux2.6下epoll是多路转接性能最好的
高效的原因:
1.底层采用回调机制来激活所关心的结点
2.维护所关心的文件描述符以及对应事件采用红黑树,进而可以减少维护该数据结构增删查改的成本
3.事件一旦就绪,相干事件发送到就绪队列,当上层epoll _wait在获取时可以以O1时间复杂度获取有效事件。
4.就绪队列用户到内核区采用内存映射机制,减少数据拷贝次数
5.就绪时间的陈列方式是从0下标开始连续陈列,避免了数据重复拷贝的问题

eopll的三个接口:
epoll_create 调用它会在底层创建一颗空的红黑树(只有根,没有结点)并维护就绪队列
epoll_ctl 可以向红黑树中添加或释放结点
epoll_wait 返回要关心的事件中哪些已经就绪(底层采用回调机制激活结点(激活方式有两种:LT水平触发(总是通知)和ET边沿触发(只通知一次))并放在就绪队列(队列是有顺序的,从左往右)中)

makefile

myepoll:myepoll.cgcc -o $@ $^
.PHONY:clean
clean:rm -f myepoll

基于LT模式下的epoll

myepoll

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>#define SIZE 64const char *msg ="HTTP/1.0 200 OK\r\n\r\n<html><h1>chao ge!</h1></html>\r\n";static void usage(const char *proc)
{printf("Usage:\n\t%s [local_ip] [local_port]\n\n",proc);
}int startup(const char *ip, int port)
{int sock = socket(AF_INET,SOCK_STREAM,0);if(sock < 0){perror("socket");exit(2);}int opt = 1;setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//设置端口复用struct sockaddr_in local;local.sin_family =AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = inet_addr(ip);if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){perror("bind");exit(3);}if(listen(sock, 10)){perror("listen");exit(4);}return sock;
}int main(int argc,char *argv[])
{if(argc != 3){usage(argv[0]);return 1;}int listen_sock = startup(argv[1],atoi(argv[2]));int epfd = epoll_create(256);//创建模型if(epfd < 0){perror("epoll_create");return 5;}printf("listen_sock: %d, epfd: %d\n",listen_sock,epfd);struct epoll_event ev;ev.events =EPOLLIN;//LTev.data.fd = listen_sock;epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);int nums = -1;int timeout =-1;struct epoll_event revs[SIZE];//返回的事件集while(1){switch((nums = epoll_wait(epfd,revs,SIZE,timeout))){case 0:printf("timeout...\n");break;case -1:perror("epoll_wait");break;default:{//at least one fd ready!int i =0;for(;i < nums;i++){int fd =revs[i].data.fd;if(fd == listen_sock &&(revs[i].events &EPOLLIN)){//listen_sock ready!struct sockaddr_in client;socklen_t len =sizeof(client);int rw_sock = accept(listen_sock,(struct sockaddr*)&client,&len);if(rw_sock < 0){perror("accept");continue;}printf("get a new client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));ev.events = EPOLLIN;ev.data.fd = rw_sock;epoll_ctl(epfd,EPOLL_CTL_ADD,rw_sock,&ev);}else if(fd!= listen_sock){//normal fd rw events ready!if(revs[i].events &EPOLLIN){//readchar buf[4096];ssize_t s = read(fd,buf,sizeof(buf)-1);if(s>0){buf[s] = 0;printf("client# %s\n",buf);ev.events =EPOLLOUT;ev.data.fd = fd;epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);}else if(s == 0){printf("client is quit!\n");close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);}else{perror("read");close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);}}else if(revs[i].events &EPOLLOUT){//writewrite(fd,msg,strlen(msg));close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL\,fd,NULL);}else{}}else{}}}break;}}close(epfd);return 0;}

基于ET模式下的epoll

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define SIZE 64typedef struct fd_buff//为每一个文件描述符设置一个缓冲区
{int fd;char buf[4096];int step;}fd_buff_t,*fd_buff_p,**fd__buff__pp;fd_buff_p alloc_buff(int sock)
{fd_buff_p tmp =(fd_buff_p)malloc(sizeof(fd_buff_t));if(!tmp){return NULL;}tmp->fd = sock;tmp->step = 0;
}void delete_buff(fd_buff_p fp)
{if(fp){free(fp);}
}const char *msg ="HTTP/1.0 200 OK\r\n\r\n<html><h1>chao ge!</h1></html>\r\n";static void usage(const char *proc)
{printf("Usage:\n\t%s [local_ip] [local_port]\n\n",proc);
}int startup(const char *ip, int port)
{int sock = socket(AF_INET,SOCK_STREAM,0);if(sock < 0){perror("socket");exit(2);}int opt = 1;setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));struct sockaddr_in local;local.sin_family =AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = inet_addr(ip);if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){perror("bind");exit(3);}if(listen(sock, 10)){perror("listen");exit(4);}return sock;
}int set_nonblock(int fd)
{int fl = fcntl(fd,F_GETFL);//获得文件描述符的标志位信息(确定它是阻塞还是非阻塞)fcntl(fd,F_SETFL,fl|O_NONBLOCK);//再调用fcntl通过F_SETFL设置其描述符,为其添加一个选项:O_NONBLOCK(非阻塞)
}int myread(int fd,char *buf,int size)
{//循环读(一次读不完)ssize_t len = 0;//当前长度ssize_t total = 0;//总共读的数据while((len = read(fd,buf+total,8))> 0&&len ==8){total +=len;if(len < 8){break;}}}int main(int argc,char *argv[])
{if(argc != 3){usage(argv[0]);return 1;}int listen_sock = startup(argv[1],atoi(argv[2]));set_nonblock(listen_sock);//设置套接字为非阻塞int epfd = epoll_create(256);if(epfd < 0){perror("epoll_create");return 5;}printf("listen_sock: %d, epfd: %d\n",listen_sock,epfd);struct epoll_event ev;ev.events =EPOLLIN|EPOLLET;// |epollet设置为ET模式(默认为LT)ev.data.ptr = alloc_buff(listen_sock);epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);int nums = -1;int timeout =-1;struct epoll_event revs[SIZE];while(1){switch((nums = epoll_wait(epfd,revs,SIZE,timeout))){case 0:printf("timeout...\n");break;case -1:perror("epoll_wait");break;default:{//at least one fd ready!int i =0;for(;i < nums;i++){int fd =((fd_buff_p)(revs[i].data.ptr))->fd;if(fd == listen_sock &&(revs[i].events &EPOLLIN)){//listen_sock ready!struct sockaddr_in client;socklen_t len =sizeof(client);int rw_sock = accept(listen_sock,(struct sockaddr*)&client,&len);if(rw_sock < 0){perror("accept");continue;}printf("get a new client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));set_nonblock(rw_sock);ev.events = EPOLLIN|EPOLLET;ev.data.ptr = alloc_buff(rw_sock);epoll_ctl(epfd,EPOLL_CTL_ADD,rw_sock,&ev);}else if(fd!= listen_sock){//normal fd rw events ready!if(revs[i].events &EPOLLIN){//readchar buf[4096];ssize_t s = myread(fd,buf,sizeof(buf)-1);if(s>0){buf[s] = 0;printf("client# %s\n",buf);ev.events =EPOLLOUT|EPOLLET;ev.data.ptr = revs[i].data.ptr;epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);}else if(s == 0){printf("client is quit!\n");close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);delete_buff(revs[i].data.ptr);}else{perror("read");close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);}}else if(revs[i].events &EPOLLOUT){//writewrite(fd,msg,strlen(msg));close(fd);epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);}else{}}else{}}}break;}}return 0;}
  相关解决方案