Linux 多路转接之epoll

一、epoll
作为多路转接中最晚出现的、也是性能最好的多路I/O就绪通知方法,epoll 继承了select和poll的优点,并且克服了select的描述符数量上限和poll的线性扫描的缺点。

其实现机制是通过两个数据结构,红黑树保存所有的描述符,就绪队列则只保存已经就绪的描述符。

二、接口
1. int epoll_create(int size)

创建⼀个epoll的句柄。⾃从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,它就是会占⽤⼀个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使⽤完epoll后,必须调⽤close()关闭,否则可能导致fd被耗尽。

2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,⽽是在这⾥先注册要监听的事件类型。
第⼀个参数是epoll_create()的返回值。
第⼆个参数表⽰动作,⽤三个宏来表⽰:

EPOLL_CTL_ADD:注册新的fd到epfd中;

EPOLL_CTL_MOD:修改已经注册的fd的监听事件;

EPOLL_CTL_DEL:从epfd中删除⼀个fd;

第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事。struct epoll_event结构体如下:

struct epoll_event  
{  
  __uint32_t   events; /* Epoll events */  
  epoll_data_t data;   /* User data variable */  
};

events可以是以下⼏个宏的集合:

EPOLLIN :表⽰对应的⽂件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表⽰对应的⽂件描述符可以写;

EPOLLPRI:表⽰对应的⽂件描述符有紧急的数据可读(这⾥应该表⽰有带外数据到来);

EPOLLERR:表⽰对应的⽂件描述符发⽣错误;

EPOLLHUP:表⽰对应的⽂件描述符被挂断;

EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于⽔平触发(LevelTriggered)来说的。

EPOLLONESHOT:只监听⼀次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加⼊到EPOLL队列⾥。

3.int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)

等待在epoll监控的事件中已经发生的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发⽣的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在⽤户态中分配内存)。maxevents告之内核这个events有多⼤,这个 maxevents的值不能⼤于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会⽴即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调⽤成功,返回对应I/O上已准备好的⽂件描述符数⽬,如返回0表⽰已超时。

epoll的工作方式:
LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况。
ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。

三、代码
1.server端

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>

static void Usage(const char *proc)
{
    printf("Usage:%s [local_ip][local_port]\n",proc);
}

typedef struct fd_buf{
    int fd;
    char buf[10240];
}fd_buf_t,*fd_buf_p;

static void *alloc_fd_buf(int fd)
{
    fd_buf_p tmp = (fd_buf_p)malloc(sizeof(fd_buf_t));
    if(tmp){
        perror("malloc");
        return NULL;
    }
    tmp->fd = fd;
    return tmp;
}

int startup(const char *_ip,int _port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock < 0){
        perror("socket");
        exit(2);
    }
    int opt = 3;
    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) < 0){
        perror("isten");
        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 epollfd = epoll_create(256);
    if(epollfd < 0){
        perror("epoll_create");
        close(listen_sock);
        return 5;
    }
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = alloc_fd_buf(listen_sock);
    epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&ev);

    int nums = 0;
    struct epoll_event evs[64];
    int timeout = -1;
    while(1){
        switch((nums = epoll_wait(epollfd,evs,64,timeout))){
            case -1:
                perror("epoll wait");
                break;
            case 0:
                printf("timeout...!\n)");
                break;
            default:
                {
                    int i = 0 ;
                    for(;i < nums;i++){
                        fd_buf_p fp = (fd_buf_p)evs[i].data.ptr;
                        if(fp->fd == listen_sock &&\
                                (evs[i].events & EPOLLIN)){
                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);
                            int new_sock = accept(listen_sock,\
                                    (struct sockaddr*)&client,&len);
                            if(new_sock < 0){
                                perror("accept");
                                continue;
                            }
                            printf("get a new client!\n");
                            ev.events = EPOLLIN;
                            ev.data.ptr = alloc_fd_buf(new_sock);
                            epoll_ctl(epollfd,EPOLL_CTL_ADD,\
                                    new_sock,&ev);
                        }
                        else if(fp->fd != listen_sock){
                            if(evs[i].events & EPOLLIN){
                                ssize_t s = read(fp->fd,fp->buf,\
                                        sizeof(fp->buf));
                                        if(s > 0){
                                            fp->buf[s] = 0;
                                            printf("client say:%s\n",fp->buf);
                                            ev.events = EPOLLOUT;
                                            ev.data.ptr = fp;
                                            epoll_ctl(epollfd,EPOLL_CTL_MOD,\
                                                    fp->fd,&ev);
                                        }else if(s <= 0){
                                            close(fp->fd);
                                            epoll_ctl(epollfd,EPOLL_CTL_DEL,\
                                                    fp->fd,&ev);
                                            free(fp);
                                        }else{

                                        }
                            }else if(evs[i].events & EPOLLOUT){
                                const char *msg = "HTTP/1.0 200 OK\r\n\r\n<html><h1>hello lp</h1></html>";
                                write(fp->fd,msg,strlen(msg));
                                close(fp->fd);
                                epoll_ctl(epollfd,EPOLL_CTL_DEL,\
                                        fp->fd,NULL);
                                free(fp);
                            }else{

                            }
                        }else{

                        }
                    }
                }
                break;
        }
    }
    return 0;
}

2.client端

   #include<stdio.h>  
    #include<sys/types.h>  
    #include<netinet/in.h>  
    #include<arpa/inet.h>  
    #include<sys/socket.h>  
    #include<stdlib.h>  
    #include<string.h>  
    #include<strings.h>  
    #include<sys/stat.h>  
    #include<unistd.h>  
    static void Usage(char * proc)  
    {  
        printf("Usage : %s [ip] [port]\n");  
    }  

    int main(int argc, char* argv[])  
    {  
        if(argc != 3)  
        {  
            Usage(argv[0]);  
            return 1;  
        }  

        int sock = socket(AF_INET, SOCK_STREAM, 0);  
        struct sockaddr_in peer;  
        peer.sin_family = AF_INET;  
        peer.sin_port = htons(atoi(argv[2]));  
        peer.sin_addr.s_addr = inet_addr(argv[1]);  

        if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)  
        {  
            perror("connect");  
            return 2;  
        }  

        char buf[10240];  

        while(1)  
        {  
            printf("Please Enter : ");  
            fflush(stdout);  
            ssize_t s = read(0, buf, sizeof(buf)-1);  
            int sfd = dup(STDOUT_FILENO);  
            if(s > 0)  
            {  
                buf[s-1] = 0;  

               int new_fd = dup2(sock, 1);  
                if(new_fd == -1)  
                {  
                   perror("dup()");  
                   return -1;  
                }  
                printf("%s",buf);  
                fflush(stdout);  

                dup2(sfd, STDOUT_FILENO);  

                ssize_t _s = read(sock, buf, sizeof(buf)-1);  
                if(_s > 0)  
                {  
                   buf[_s] = 0;  
                   printf("sever # %s \n", buf);  
                }  
            }  
        }  
        close(sock);  
        return 0;  
    }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值