I/O多路复用--epoll用法详解

epoll是内核2.6版本提出来的,比poll和select更加灵活,而且没有文件描述符的限制

int epoll_create(int size);//创建一个epoll句柄,size告诉内核这个监听的数目一共多大,成功返回一个文件文件符的句柄,失败返回-1.
int epoll_ctl(int epfd, intopt ,int fd, struct epoll_event *event);//epoll事件注册
第一个参数epfd是epoll_create的返回值
第二个: EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL
第三个是要监听的fd
第四个是告诉内核要监听的是什么事件
struct epoll_event {
__uint32_t events;
epoll_data_t data;
}

events可以是以下几个宏的集合
EPOLLIN: 标示对应的文件描述符刻度(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可写
EPOLLPRI:表示对应的文件描述符有紧急的数据可写(表示有带外数据到来)
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂冠
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还要继续监听这个socket的话
,需要再次把这个socket加入到EPOLL队列

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
等待事件的产生,类似于select的调用,参数events用来从内核中得到时间的集合,maxevents告知内核这个event有多大,
max_events的值不能大于epoll_create()时候的size,timeout是超时间
如果返回0,表示超时
如果返回-1,表示epoll出错,退出(错误原因可用errno查看)
如果返回非零整数,表示是需要处理的事件数量

工作模式 LT(level triger)和ET(edge trigger),LT模式是默认模式,
区别:
LT 模式只要有未处理的数据就会有通知(如缓冲区中有数据,第一次没有读取完,
那么下次还会受到通知)
ET 模式只有状态变化才会发出通知(如文件描述符有不可读变为可读时候,就会收到通知,
要一次性吧数据处理完,否则就算缓冲区里面还有数据,下次也不会通知)

**ET模式下可以减少文件描述符返回的数量,也能减少epoll相关的系统调用,
但是与LT模式比较来说,性能差异不大,ET模式逻辑复杂,出错率高,所以建议使用LT模式**

获得通知的条件:
与select/poll类似,当以下的条件发生的时候,epoll监控的相应的文件描述符会变得可读/写

取到数据的长度
2)当读半部分关闭的时候(也就是对端发送了一个FIN分节过来),此时虽然可读,但是调用recv的时候返回的是
0(腾讯面试被问过),这时候服务端可以把相应的socket关闭
3)套接字发生错误,此时recv返回的是-1,可以通过errno获得错误原因,或者通过SOL_ERROR调用getsockopt获取错误并处理
4)连接完成队列不为空,那么accpet肯定不会阻塞
对于发送,也是类似的条件,但是要注意,如果一个套接字发送的时候,如果写半部分部分关闭,对这样的套接字发送数据会产生SIGPIPE信号,系统对这个信号的默认处理是结束进程

#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
const int MaxFd = 2048;

int main() {
    int epollfd;
    epoll_event events[MaxFd], ev;
    sockaddr_in srvAddr;
    sockaddr_in cliAddr;

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    srvAddr.sin_port = htons(6000);

    if(bind(fd, (sockaddr*)&srvAddr, sizeof(sockaddr)) < 0) {
        fprintf(stderr, "bind Error:%s\n", strerror(errno));
        return 1;
    }
    if(listen(fd, 16) < 0) {
        fprintf(stderr, "listen Error: %s\n", strerror(errno));
        return 1;
    }

    epollfd = epoll_create(MaxFd);
    if(epollfd < 0) {
        fprintf(stderr, "epoll_create Error: %s\n", strerror(errno));
        return 1;
    }

    ev.events = EPOLLIN;
    ev.data.fd = fd;

    if( epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        fprintf(stderr, "epoll_ctl Error: %s\n", strerror(errno));
        return 1;
    }
    for(;;) {
        int nfds = epoll_wait(epollfd, events, MaxFd, 10);
        if(nfds < 0) {
            fprintf(stderr, "epoll_wait Error: %s\n", strerror(errno));
            return 1;
        }

        for(int i = 0; i < nfds; i++) {
            if(events[i].data.fd == fd) {
                socklen_t len;
                int newfd = accept(fd, (sockaddr*)&cliAddr, &len);
                if(newfd < 0) {
                    fprintf(stderr, "accept Error: %s\n", strerror(errno));
                }
                ev.data.fd = newfd;
                ev.events = EPOLLIN;
                fcntl(newfd, F_SETFL, O_NONBLOCK);

                if(epoll_ctl(epollfd, EPOLL_CTL_ADD, newfd, &ev) < 0) {
                    fprintf(stderr, "epoll_ctl Error: %s\n", strerror(errno));
                    return -1;
                }
                printf("new connection: %s:%d current connetion:%d\n", inet_ntoa(cliAddr.sin_addr), htons(cliAddr.sin_port), count);
            } else{
                if(events[i].events & EPOLLIN) {
                    char buf[256];
                    memset(buf, 0, sizeof(buf));
                    int ret = recv(events[i].data.fd, buf, sizeof(buf), 0);
                    if(ret == 0) {//client close
                        ev.data.fd = events[i].data.fd;
                        ev.events = EPOLLIN;
                        if( epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev) < 0) {
                            fprintf(stderr, "epoll_ctl Error: %s\n", strerror(errno));
                        }
                        close(events[i].data.fd);
                    }else if(ret < 0) {
                        ev.data.fd = events[i].data.fd;
                        ev.events = EPOLLIN;
                        if( epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev) < 0) {
                            fprintf(stderr, "epoll_ctl Error: %s\n", strerror(errno));
                        }
                        close(events[i].data.fd);
                    }
                    else
                        printf("Message: %s\n", buf);
                }
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值