这篇文章我们来介绍一下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.

Logo

更多推荐