epoll()
epoll与select和poll不同,它将检测文件描述符的变化委托给内核去处理, 然后内核将发生变化的文件描述符对应的事件返回给应用程序
#include <sys/epoll.h>
int epoll_create(int size);
函数说明: 创建一个epoll实例,即一个epoll树根
参数说明:
size: 最大节点数, 此参数在linux 2.6.8已被忽略, 但必须传递一个大于0的数.给一个大于0的数就行
返回值:
成功: 返回一个大于0的文件描述符, 代表整个树的树根.
失败: 返回-1, 并设置errno值.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
函数说明: 对epoll树的事件节点的添加, 删除和修改
参数说明:
epfd: epoll树根,由 epoll_create 创建
op:
EPOLL_CTL_ADD: 添加事件节点到树上
EPOLL_CTL_DEL: 从树上删除事件节点
EPOLL_CTL_MOD: 修改树上对应的事件节点
fd: 事件节点对应的文件描述符
event: 要操作的事件节点
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
event.events常用的有:
EPOLLIN: 读事件
EPOLLOUT: 写事件
EPOLLERR: 错误事件
EPOLLET: 边缘触发模式
event.data.fd: 要监控的事件对应的文件描述符
返回值:
成功: 返回0
失败: 返回-1, 并设置errno值.
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
函数说明:等待内核返回准备好的IO事件
参数说明:
epfd: epoll树根
events: 传出参数, 其实是一个事件结构体数组
maxevents: 数组大小
timeout:
-1: 表示永久阻塞
0: 立即返回
>0: 表示超时等待事件
返回值:
成功: 返回发生事件的个数
失败: 若timeout=0, 没有事件发生则返回; 返回-1, 设置errno值
epoll_wait的events是一个传出参数, 调用epoll_ctl传递给内核什么值, 当epoll_wait返回的时候, 内核就传回什么值,不会对struct event的结构体变量的值做任何修改.
代码:
#include "socketwrap.h"
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
int main()
{
int sfd = Socket(AF_INET, SOCK_STREAM, 0);
// 设置端口复用
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
struct sockaddr_in soaddr;
bzero(&soaddr, sizeof(soaddr));
soaddr.sin_family = AF_INET;
soaddr.sin_port = htons(9999);
soaddr.sin_addr.s_addr = htonl(INADDR_ANY);
Bind(sfd, (struct sockaddr *)&soaddr, sizeof(soaddr));
// 监听-listen
Listen(sfd, 128);
struct epoll_event ev;
struct epoll_event events[1024];
// 创建一棵epoll树
int epfd = epoll_create(1024);
if (epfd < 0)
{
perror("create epoll error");
return -1;
}
// 将lfd上epoll树
int ret;
int nready;
ev.data.fd = sfd;
ev.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);
if (ret < 0)
{
perror("create epoll error");
return -1;
}
int i;
int sockfd, cfd;
struct sockaddr_in clientsocket;
socklen_t clilen;
char buff[64];
while (1)
{
clilen = sizeof(clientsocket);
bzero(&clientsocket, clilen);
nready = epoll_wait(epfd, events, 1024, -1);
if (nready < 0)
{
perror("epoll_wait error");
if (errno == EINTR)
{
continue;
}
break;
}
int n;
for (i = 0; i < nready; i++)
{
sockfd = events[i].data.fd;
if (sockfd == sfd)
{
cfd = Accept(sfd, (struct sockaddr *)&clientsocket, &clilen);
bzero(&ev, sizeof(ev));
ev.data.fd = cfd;
ev.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
if (ret < 0)
{
perror("add epoll node error");
return -1;
}
// 打印客户端的IP和PORT
char sIP[16];
memset(sIP, 0x00, sizeof(sIP));
printf("client [%s:%d] connect\n", inet_ntop(AF_INET, &clientsocket.sin_addr.s_addr, sIP, sizeof(sIP)), htons(clientsocket.sin_port));
continue;
}
// 有客户端发送数据过来
memset(buff, 0x00, sizeof(buff));
n = Read(sockfd, buff, sizeof(buff));
// n = recv(sockefd, buff, sizeof(buff), 0);
if (n < 0)
{
perror("read over");
bzero(&ev, sizeof(ev));
ev.data.fd = sockfd;
ev.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev);
if (ret < 0)
{
perror("del epoll node error");
return -1;
}
close(sockfd);
}
else if (n == 0)
{
// printf("client is closed\n");
bzero(&ev, sizeof(ev));
ev.data.fd = sockfd;
ev.events = EPOLLIN;
ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev);
if (ret < 0)
{
perror("del epoll node error");
return -1;
}
close(sockfd);
}
else
{
printf("[%d]:[%s]\n", n, buff);
for (i = 0; i < n; i++)
{
buff[i] = toupper(buff[i]);
}
Write(sockfd, buff, n);
}
}
}
close(epfd);
close(sfd);
return 0;
}