Linux下实现epoll服务器和缺点总结
这篇文章我们来介绍一下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.
更多推荐
已为社区贡献1条内容
所有评论(0)