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,不再需要检查所有监听的文件描述符。