LINUX下IO复用——EPOLL

epoll_create函数

//创建epoll实例。返回新实例的文件描述符。
//size:指定文件数量
//epoll_create()返回的文件描述符应该用close()关闭
int epoll_create (int __size)

注:Linux2.6.8之后__size被忽略掉了,大于0即可
linux手册对epoll_create函数的描述

epoll_ctl函数

epfd:epoll_create返回的文件描述符
op:
EPOLL_CTL_ADD 添加文件描述符
EPOLL_CTL_DEL 删除文件描述符
EPOLL_CTL_MOD 修改文件描述符对应的事件
fd:文件描述符
event:需要监听的事件

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

epoll_wait函数

//等待epoll实例epfd上的事件。
//函数正确:返回触发事件的个数,并将事件复制到events中。
//错误  :返回-1并将errno变量设置为特定错误代码。
//maxevents:events的最大容量
//timeout:指定以毫秒为单位的最大等待时间(-1 == infinite)
int epoll_wait (int epfd, struct epoll_event *events, int maxevents, int timeout)

代码

my_epoll.h

class CEpoll
{
public:
    CEpoll(short sPort);
    ~CEpoll();
    bool run();
private:
    int  SetNonblock(int fd);
    void AddTooEpoll(int fd);
    void DelFromEpoll(int fd);
private:
    int     m_fdEpoll{0};
    int     m_fdListen{0};
    short   m_sPort{0};
};

my_epoll.cpp
#include "my_epoll.h"
#include <sys/epoll.h>  //epoll_create() 参数不等于0就行
#include <fcntl.h>      //fcntl()
#include <unistd.h>     //close()
#include <sys/socket.h> //socket()
#include <arpa/inet.h>  //inet_pton
#include <string.h>
#include <iostream>
#include <vector>

CEpoll::CEpoll(short sPort) : m_sPort(sPort)
{
    m_fdEpoll = epoll_create(10);
}

CEpoll::~CEpoll()
{
    if (m_fdEpoll > 0)
        close(m_fdEpoll);
}

int CEpoll::SetNonblock(int fd)
{
    int opt = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, opt | O_NONBLOCK);
    return opt;
}

void CEpoll::DelFromEpoll(int fd)
{
    epoll_ctl(m_fdEpoll, EPOLL_CTL_DEL, fd, nullptr);
}

void CEpoll::AddTooEpoll(int fd)
{
    epoll_event evn;
    evn.data.fd = fd;
    evn.events = EPOLLIN | EPOLLET;
    epoll_ctl(m_fdEpoll, EPOLL_CTL_ADD, fd, &evn);
}

bool CEpoll::run()
{
    do
    {
        m_fdListen = socket(AF_INET, SOCK_STREAM, 0);
        if (m_fdListen == -1)
            break;

        sockaddr_in addr;
        bzero(&addr, sizeof(addr));
        addr.sin_family         = AF_INET;
        addr.sin_port           = htons(m_sPort);
        addr.sin_addr.s_addr    = INADDR_ANY;
        if (-1 == bind(m_fdListen, (sockaddr *)&addr, sizeof(addr)))
            break;

        if (-1 == listen(m_fdListen, 5))
            break;

        AddTooEpoll(m_fdListen);

        std::vector<epoll_event> events(1);
        while (true)
        {
            int nConn{0};
            int nClose{0};
            
            int nCount = epoll_wait(m_fdEpoll, events.data(), events.size(), -1);

            if (nCount < 0)
                break;

            for (int i = 0; i < nCount; i++)
            {
                epoll_event &evn = events[i];
                if (evn.data.fd == m_fdListen && evn.events & EPOLLIN)
                {
                    sockaddr_in addrClt;
                    socklen_t nLen = sizeof(sockaddr_in);
                    int fd = accept(m_fdListen, (sockaddr *)&addrClt, &nLen);

                    if (-1 == fd)
                    {
                        std::cout << errno << ":" << strerror(errno) << std::endl;
                        continue;
                    }
                    nConn++;
                    std::cout << inet_ntoa(addrClt.sin_addr) << "," << addrClt.sin_port
                              << " connected!" << std::endl;

                    SetNonblock(fd);
                    AddTooEpoll(fd);
                    continue;
                }
                sockaddr_in addrClt;
                socklen_t nLen = sizeof(sockaddr_in);
                getpeername(evn.data.fd, (sockaddr *)&addrClt, &nLen);
                if ((evn.events & EPOLLHUP)/*异常关闭*/ || (evn.events & EPOLLRDHUP)/*对端调用close 或shutdown*/)
                {
                    std::cout << inet_ntoa(addrClt.sin_addr) << "," << addrClt.sin_port
                              << " closed!(" << evn.events<<")"<<std::endl;

                    DelFromEpoll(evn.data.fd);
                    close(evn.data.fd);
                    nClose++;
                    continue;
                }

                if (evn.events & EPOLLIN)
                {
                    char buf[256]{0};
                    while (true)
                    {
                        int nBytes = recv(evn.data.fd, buf, sizeof(buf), 0);
                        if (nBytes == -1)
                        {
                            if (errno == EAGAIN || errno == EWOULDBLOCK)
                                continue;
                            std::cout << errno << ":" << strerror(errno) << std::endl;
                            break;
                        }
                        if (0 == nBytes)
                        {
                             std::cout << inet_ntoa(addrClt.sin_addr) << "," << addrClt.sin_port
                              << " closed!" << std::endl;
                            DelFromEpoll(evn.data.fd);
                            close(evn.data.fd);
                            nClose++;
                        }
                        break;
                    }
                    int nRcv = strlen(buf);
                    if (nRcv > 0)
                    {
                        std::cout << inet_ntoa(addrClt.sin_addr) << "," << addrClt.sin_port<<" : "<<buf<<std::endl;
                        send(evn.data.fd, buf, nRcv + 1, 0);
                    }
                        
                }
            }

            events.resize(events.size() + (nConn - nClose));
        }

        return true;
    } while (false);

    std::cout << errno << ":" << strerror(errno) << std::endl;
    return false;
}
测试
#include"my_epoll.h"

int main(int argc,char *argv[])
{
    CEpoll epoll(1025);
    epoll.run();
    return 0;
}

与select 和 poll相比 epoll只返回触发事件的fd,不再需要检查所有监听的文件描述符。

 select 代码实现
 poll 代码实现

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux IO复用是指在处理多个I/O事件时,通过一种机制使得一个进程可以同时监听多个I/O操作,从而提高程序的效率和性能。 在Linux系统中,常用的IO复用机制有三种:select、poll和epoll。 1. select:select函数是最早引入的IO复用机制之一,它通过传入一组文件描述符集合,来监听这些文件描述符上是否有事件发生。当其中任意一个文件描述符上有事件发生时,select函数就会返回,然后程序可以通过遍历文件描述符集合来判断哪些文件描述符上有事件发生。 2. poll:poll函数是对select的改进,其使用方式和select类似。不同的是,poll函数使用一个pollfd结构数组来存储待监听的文件描述符及其对应的感兴趣事件,通过调用poll函数时传入这个数组来实现IO复用。相对于select,poll没有最大文件描述符数量的限制,并且效率更高。 3. epollepollLinux下最新的IO复用机制,它提供了更加高效的IO事件通知机制。epoll使用一个文件描述符来管理被监听的其他文件描述符,通过调用epoll_ctl函数向这个文件描述符中注册或者删除需要监听的文件描述符。当某个文件描述符上有事件发生时,epoll_wait函数会返回该文件描述符的相关信息给程序处理。相对于select和poll,epoll在处理大量连接时具有更好的性能。 总结来说,Linux IO复用机制可以让一个进程同时监听多个I/O事件,避免了使用阻塞IO时的等待时间,提高了程序的效率和性能。而select、poll和epoll是常用的IO复用机制。其中,epoll是效率最高的一种机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值