以下內(nèi)容是參考《linux/unix系統(tǒng)編程手冊》對epoll的一個個人總結。
一、epoll的優(yōu)點
同I/O多路復用和信號驅動I/O一樣,linux的epoll API可以檢查多個文件描述符上的I/O就緒狀態(tài)。epoll API的主要優(yōu)點
1.當有大量的文件描述符需要檢查時,epoll的性能延展性比select()和epoll(高很多)
2.epoll API既支持水平觸發(fā)也支持邊緣觸發(fā),與之相反,select和poll只支持水平觸發(fā),而信號驅動I/O只支持邊緣觸發(fā)
3.可以避免復雜的信號處理流程(比如信號隊列溢出時的處理)
4.靈活性高,可以指定我們希望檢查的時間類型
二、epoll系統(tǒng)調用組成
1.epoll_create()創(chuàng)建一個epoll實例,返回代表該實例的文件描述符
2.epoll_ctl()操作同epoll實例相關聯(lián)的興趣列表
3.epoll_wait()返回與epoll實例相關聯(lián)的就緒列表中的成員
#include <sys/epoll.h>
int epoll_create(int size);
功能:創(chuàng)建一個新的epoll實例,其對應的興趣列表初始化為空
參數(shù):size 想要檢查文件描述符的個數(shù)(linux2.6.8之后該參數(shù)不再使用)
返回值:成功文件描述符,失敗-1
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:修改epoll的興趣列表
參數(shù):epfd epoll_create的返回值
fd 要修改的文件描述符(可以是無名管道、有名管道、套接字、消息隊列、終端、設備等,但是不能是普通文件或目錄的文件描述符)
op:
EPOLL_CTL_ADD 添加fd到興趣列表
EPOLL_CTL_MOD 修改已經(jīng)注冊的fd的監(jiān)聽事件;
EPOLL_CTL_DEL 從epfd中刪除一個fd
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
常用的事件類型:
EPOLLIN :表示對應的文件描述符可以讀;
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數(shù)據(jù)可讀
EPOLLERR:表示對應的文件描述符發(fā)生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
EPOLLET: 表示對應的文件描述符有事件發(fā)生;
返回值:成功0 失敗-1
#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能:返回epoll實例中處于就緒態(tài)的文件描述符信息,單個epoll_wait()調用能返回多個就緒態(tài)文件描述符的信息。
參數(shù):epfd epoll實例
events 存放文件描述符的信息
maxevents 文件描述符的大個數(shù)
timeout -1 阻塞等待
0 非阻塞
>0 阻塞的大時間
返回值:成功0 失敗-1
三、網(wǎng)絡中的應用實例
1.net.h
#ifndef _NET_H_
#define _NET_H_
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <pthread.h>
#define MAX_EVENTS 500
#endif
2.server.c
#include "head.h"
int sockfd, acceptfd,listenfd;
struct sockaddr_in serveraddr, clientaddr;
socklen_t len = sizeof(serveraddr);
struct epoll_event ev, events[MAX_EVENTS];
int rv,i;
int epollfd,fds;
int tcp_create_socket(void)
{
//1、設定基于TCP通信的標準
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("fail to socket");
return -1;
}
bzero(&serveraddr, len);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("8000"));//端口號
serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199"); //IP地址
//2、綁定IP地址和端口號
if(bind(sockfd, (struct sockaddr*)&serveraddr, len) < 0)
{
perror("fail to bind");
close(sockfd);
return -1;
}
//3、設置監(jiān)聽描述符的個數(shù)
if(listen(sockfd, 5) < 0)
{
perror("fail to listen");
close(sockfd);
return -1;
}
return sockfd;
}
int mz_process_data(int acceptfd)//數(shù)據(jù)處理
{
int bytes = 0;
char buf[100];
bytes = recv(acceptfd, buf, 100, 0);
printf("----------------\n");
if(bytes < 0)
{
perror("recv error");
return -1;
}
if(bytes == 0) //說明客戶端放棄鏈接
{
return -2;
}
printf("client:%s\n", buf);
return 0;
}
int main(int argc, const char *argv[])
{
epollfd = epoll_create(MAX_EVENTS);//創(chuàng)建對象
if(epollfd < 0)
{
perror("fail to epoll!");
return -1;
}
listenfd = tcp_create_socket();
fcntl(listenfd,F_SETFL, O_NONBLOCK);//設置非阻塞
ev.data.fd = listenfd;
ev.events = EPOLLIN;
rv = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);//加入監(jiān)聽事件
if (rv < 0)
{
perror("epoll_ctl err:");
return -1;
}
while(1)
{
fds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if(fds < 0)
{
perror("epoll_wait error");
return -1;
}
//輪尋被激活的套接子
for(i = 0; i < fds; i++)
{
if(events[i].data.fd == listenfd) //判斷是否是監(jiān)聽的對象(描述符)
{
acceptfd = accept(listenfd, (struct sockaddr*)&clientaddr, &len);
if(acceptfd < 0)
{
perror("accept error");
continue;
}
ev.data.fd = acceptfd;
ev.events = EPOLLIN | EPOLLET;
//將鏈接的客戶端添加到關注的事件中去
epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptfd, &ev);
continue;
}
else
{
rv = mz_process_data(events[i].data.fd);
if(rv == -2) //客戶端放棄鏈接,將描述符從事件中清除
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
close(events[i].data.fd);
continue;
}
}
}
}
return 0;
}
3.client.c
#include "head.h"
#define N 32
int main(int argc, const char *argv[])
{
int sockfd;
struct sockaddr_in serveraddr;
socklen_t len = sizeof(serveraddr);
char buf[N] = {0};
//1、設定基于TCP通信的標準
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("fail to socket");
return -1;
}
bzero(&serveraddr, len);
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi("8000"));//端口號
serveraddr.sin_addr.s_addr = inet_addr("192.168.8.199"); //IP地址
//2、綁定IP地址和端口號
if(connect(sockfd, (struct sockaddr*)&serveraddr, len) < 0)
{
perror("fail to connect");
close(sockfd);
return -1;
}
//客戶端用來發(fā)送數(shù)據(jù)
while(1)
{
fgets(buf, N, stdin);
buf[strlen(buf) - 1] = '\0';
send(sockfd, buf, N, 0);
if(strncmp(buf, "quit", 4) == 0)
{
break;
}
}
close(sockfd);
return 0;
}