1. 程式人生 > >Linux下實現epoll伺服器和缺點總結

Linux下實現epoll伺服器和缺點總結

這篇文章我們來介紹一下epoll實現多使用者伺服器端
先列出epoll的API:
epoll和poll,select有些不同,通過三個函式來進行實現的:
這裡寫圖片描述
就是這張圖片裡面的三個函式
(1)epoll_create(size)介紹是:
這裡寫圖片描述
(2)epoll_ctl(int epfds,int op,int fd,struct epoll_event *event);
這裡寫圖片描述
(3)epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
這裡寫圖片描述
伺服器程式碼如下,沒有實現封裝。這樣便於理解一些。

#include<stdio.h>
#include <stdlib.h>  
#include <unistd.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <string.h>   
#include <sys/socket.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/epoll.h>
#define MAX_EVENTS 256 
typedef struct fd_buff { int fd; char buff[1024]; } fd_buff_t,*fd_buff_p; struct epoll_event evts[256]; static void usage(char* proc) { printf("usage:%s[server ip][server port]",proc); } int startup(char* ip,int port) { int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"
); exit(2); } int op=1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&op,sizeof(op)); struct sockaddr_in server_addr; server_addr.sin_family=AF_INET; server_addr.sin_port=htons(port); server_addr.sin_addr.s_addr=inet_addr(ip); if(bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock; } fd_buff_p new_fd_buff(int _sock) { fd_buff_p ret =(fd_buff_p)malloc(sizeof(fd_buff_t)); ret->fd=_sock; memset(ret->buff,0,sizeof(ret->buff)); return ret; } int main(int argc,char* argv[]) { if( argc!=3) { usage(argv[0]); exit(1); } int listen_sock=startup(argv[1],atoi(argv[2]));//封裝一個函式去獲取監聽套接字和select和poll的套路一樣,不詳細說明 int epollfds=epoll_create(MAX_EVENTS);//建立epoll模型,其實epoll的內部使用的資料結構是紅黑樹,還有一個連結串列儲存準備好的檔案描述符結構 if(epollfds<0) { perror("epoll_create"); exit(5); } struct epoll_event evt; evt.events=EPOLLIN; evt.data.ptr=new_fd_buff(listen_sock); epoll_ctl(epollfds,EPOLL_CTL_ADD,listen_sock,&evt); int nfds=0; int timeout=10000; while(1) { nfds=epoll_wait(epollfds,evts,MAX_EVENTS,timeout); if(nfds<0) { perror("epoll_wait"); exit(6); } else if(nfds==0) { printf("timeout...\n"); } else { int idx=0; for(;idx<nfds;++idx) { fd_buff_p fp=evts[idx].data.ptr; if(fp->fd==listen_sock && evts[idx].events & EPOLLIN) { //fds_access()//準備接受外部連結 struct sockaddr_in client_addr; socklen_t len=sizeof(client_addr); int new_sock=accept(listen_sock,(struct sockaddr*)&client_addr,&len); if(new_sock<0) { perror("accept"); exit(7); } printf("new client connected\n"); fflush(stdout); struct epoll_event _event; _event.events=EPOLLIN; _event.data.ptr=new_fd_buff(new_sock); epoll_ctl(epollfds,EPOLL_CTL_ADD,new_sock,&_event); } else if(fp->fd!=listen_sock) { if(evts[idx].events& EPOLLIN) { int s=read(fp->fd,fp->buff,sizeof(fp->buff)); if(s>0) { fp->buff[s]=0; printf("client:%s",fp->buff); struct epoll_event new_event; new_event.events=EPOLLOUT; new_event.data.ptr=fp; epoll_ctl(epollfds,EPOLL_CTL_MOD,fp->fd,&new_event); } if(s<=0) { printf("client quit\n"); close(fp->fd); epoll_ctl(epollfds,EPOLL_CTL_DEL,fp->fd,NULL); free(fp); } } else if(evts[idx].events&EPOLLOUT) { const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><h1>Hello epoll!</h1></html>"; write(fp->fd,msg,strlen(msg)); close(fp->fd); epoll_ctl(epollfds,EPOLL_CTL_DEL,fp->fd,NULL); free(fp); } } } } } return 0; }

程式執行結果:
伺服器端執行結果:
這裡寫圖片描述
客戶執行結果:
這裡寫圖片描述
在網頁開啟為:
這裡寫圖片描述
缺點:
1. 相對select來說, epoll的跨平臺性不夠用 只能工作在linux下, 而select可以在windows linux apple上使用, 還有手機端android iOS之類的都可以. android雖然是linux的核心 但早期版本同樣不支援epoll的.
2. 相對select來說 還是用起來還是複雜了一些, 不過和IOCP比起來 增加了一點點的複雜度卻基本上達到了IOCP的併發量和效能, 而複雜度遠遠小於IOCP.
3. 相對IOCP來說 對多核/多執行緒的支援不夠好, 效能也因此在效能要求比較苛刻的情況下不如IOCP.