epoll的相关系统调用
1、int epoll_create(int size);
创建一个epoll描述符,该描述符占用一个fd值,程序退出前必须调用close()关闭,参数size为最大监听事件数
2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而要在监听之前先注册要监听的事件,函数成功调用返回0,失败返回-1
参数一epfd是epoll_create返回的描述符
参数二op表示操作
EPOLL_CTL_ADD:注册新的fd到epfd中
EPOLL_CTL_DEL:从epfd中删除fd
EPOLL_CTL_MOD:修改已经注册到epfd的监听事件
参数三fd是要监听的fd
参数四event代表需要监听的事件类型,struct epoll_event结构体如下
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events;
epoll_data_t data;
};
events可以是以下几个宏的集合
EPOLLIN:对应的fd可读
EPOLLOUT:对应的fd可写
EPOLLPRI:对应的fd有紧急数据可读(这里应该表示有带外数据到来)
EPOLLERR:对应的fd出错
EPOLLHUP:对应的fd被挂断
EPOLLET:将EPOLL设为边缘出发(Edge Triggered)模式,这是相对水平触发(Level Triggered)来说的
EPOLLONESHOT:只监听一次,如果需要继续监听该socket,需再次把该socket加入EPOLL队列中
3、int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
收集在epoll监听的事件中已经发生的事件
参数一epfd为epoll_create返回的描述符
参数二events为一个已分配好内存的epoll_event结构体数组,epoll会将已发生的事件存入该数组,events不能是NULL
参数三maxevents为events数组的大小
参数四timeout为超时时间,单位为毫秒,0表示立即返回,-1表示永久阻塞
函数成功调用返回对应I/O上已准备好的fd数目,失败返回-1,超时返回0
/*************************************************************************
* Copyright (C): 1540999272@qq.com
* Filename: server.c
* Author: Lu Zengmeng
* Description:
* Creat Time: 2016-07-19 12:08
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/epoll.h>
#define PORT 8003
#define BUF_LEN 128
#define BACKLOG 5
#define MAXEVENTS 50
int epoll_fd;
struct epoll_event events[MAXEVENTS];
void on_accept(int sock);
void on_recv(int sock);
int main(int argc, char *argv[])
{
int sock_fd;
int yes = 1;
int timeout = 5000;
int ret;
struct sockaddr_in my_addr;
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(PORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (-1 == (sock_fd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("socket");
exit(1);
}
if (-1 == setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)))
{
perror("setsockopt");
exit(1);
}
if (-1 == bind(sock_fd, (struct sockaddr *)&my_addr, sizeof(my_addr)))
{
perror("bind");
exit(1);
}
if (-1 == listen(sock_fd, BACKLOG))
{
perror("listen");
exit(1);
}
printf("listenning...\n");
epoll_fd = epoll_create(MAXEVENTS); // 创建epoll描述符
if(epoll_fd < 0)
{
perror("epoll_create");
exit(1);
}
struct epoll_event event; //81~83 初始化需要监听的事件
event.events = EPOLLIN|EPOLLET;
event.data.fd = sock_fd;
if(-1 == epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock_fd,&event)) // 注册监听事件
{
perror("epoll_ctl");
exit(1);
}
while(1)
{
ret = epoll_wait(epoll_fd,events,MAXEVENTS,timeout); // 检查注册到epoll_fd中已发生的事件,并将发生的事件赋值到events数组中
if(ret < 0)
{
perror("epoll_wait");
break;
}
if(ret == 0)
{
printf("timeout\n");
continue;
}
int i;
for(i=0;i<ret;i++)
{
if((events[i].events & EPOLLERR)||
(events[i].events & EPOLLHUP)||
(!(events[i].events & EPOLLIN)))
{
printf("epoll error\n");
close(events[i].data.fd);
continue;
}
if(events[i].data.fd == sock_fd) // 发生sock_fd对应的事件说明有新的连接到来
{
on_accept(sock_fd);
}
else
{
on_recv(events[i].data.fd);
}
}
}
close(epoll_fd);
close(sock_fd);
return 0;
}
void on_accept(int sock)
{
int conn_fd;
conn_fd = accept(sock,NULL,NULL);
if(conn_fd < 0)
{
perror("accept");
return;
}
else
printf("new connection client[%d]\n",conn_fd);
struct epoll_event event; //147~150 将新创建的连接注册到监听列表
event.data.fd = conn_fd;
event.events = EPOLLIN|EPOLLET;
epoll_ctl(epoll_fd,EPOLL_CTL_ADD,conn_fd,&event);
return;
}
void on_recv(int sock)
{
char buf[BUF_LEN];
int ret = 0;
int len = 0;
memset(buf,0,BUF_LEN);
while(1)
{
ret = recv(sock,buf+len,BUF_LEN-len,0);
if(0 == ret)
{
len = 0;
break;
}
else if(0 > ret)
{
len = 0;
break;
}
len = len + ret;
if(len < BUF_LEN)
{
continue;
}
else
{
printf("receive %s \n",buf);
break;
}
}
strcat(buf,"\0");
printf("receive: %s from client[%d]\n",buf,sock);
return;
}