嵌入式 Linux网络编程(五)——epoll机制
一、epoll简介
epoll是在2.6内核中提出的,是select和poll的增强版本。epoll更加灵活,没有描述符限制,使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。
1、epoll函数
#include <sys/epoll.h>
int epoll_create(int size);
创建一个epoll的句柄,size表示监听的文件描述的数量
int epoll_ctl(int epfd, int op, int size, struct epoll_event *event);
epoll的事件注册函数,先注册要监听的事件类型。epfd是epoll_create()的返回值,op参数表示动作,用三个宏来表示: EPOLL_CTL_ADD:注册新的fd到epfd中; EPOLL_CTL_MOD:修改已经注册的fd的监听事件; EPOLL_CTL_DEL:从epfd中删除一个fd; fd参数是需要监听的fd,event参数是告诉内核需要监听什么事
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
EPOLLIN :表示对应的文件描述符可以读
EPOLLOUT:表示对应的文件描述符可以写 EPOLLPRI:表示文件描述符有紧急的数据可读
EPOLLERR:表示文件描述符发生错误 EPOLLHUP:表示文件描述符被挂断 EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式
EPOLLONESHOT:只监听一次事件
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。
参数events用来从内核得到事件的集合
maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size
参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。
函数返回需要处理的事件数目,如返回0表示已超时。
2、epoll工作模式
epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式。
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接字,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
二、epoll编程模型
epoll编程的基本框架:
while(1){ nfds = epoll_wait(epfd,events,20,500); for(i=0;ifd; send( sockfd, md->ptr, strlen((char*)md->ptr),0 );//发送数据 ev.data.fd=sockfd; ev.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } else { //其他的处理 } }//end of for}//end of while
程序实例:
server.c:
#include#include #include #include #include #include #include #include #include #include #include #include #include #define PORT 8888#define RT_ERR (-1)#define RT_OK 0#define SERVERIP "192.168.0.200"#define LISTEN_QUEUE 10#define BUFFER_SIZE 1024#define MAX_EVENTS 1024 int main(int argc, char *argv[]){ int listenfd, connsockfd, fd, len; char readbuf[BUFFER_SIZE]; listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd < 0) { fprintf(stderr, "socket function failed.\n"); exit(RT_ERR); } struct sockaddr_in serveraddr, clientaddr;// bzero(&clientaddr, sizeof(clientaddr)); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(PORT); serveraddr.sin_addr.s_addr = inet_addr(SERVERIP); unsigned int client_len = sizeof(struct sockaddr_in); if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { fprintf(stderr, "bind function failed.\n"); close(listenfd); exit(RT_ERR); } if(listen(listenfd,LISTEN_QUEUE) < 0) { fprintf(stderr, "listen function failed.\n"); close(listenfd); exit(RT_ERR); } int opts=fcntl(listenfd, F_GETFL); if(opts<0) { fprintf(stderr, "fcntl(sock,GETFL)\n"); exit(-1); } opts = opts|O_NONBLOCK; if(fcntl(listenfd,F_SETFL,opts)<0) { fprintf(stderr, "fcntl(sock,SETFL,opts)\n"); exit(-1); } fprintf(stdout, "The server IP is %s, listen on port: %d\n", inet_ntoa(serveraddr.sin_addr), ntohs(serveraddr.sin_port)); struct epoll_event event, events[MAX_EVENTS]; int epfd = epoll_create(MAX_EVENTS); event.data.fd=listenfd; event.events=EPOLLIN|EPOLLET; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event); int nepfd, n; while(1) { nepfd = epoll_wait(epfd, events, MAX_EVENTS, 0); for(fd = 0; fd < nepfd; fd++) { bzero(&clientaddr, sizeof(clientaddr)); fprintf(stdout, "fd is %d, listenfd is %d\n", fd, listenfd); if(events[fd].data.fd == listenfd)//新的请求连接用户 { connsockfd = accept(listenfd, (struct sockaddr *)&clientaddr, &len); if(connsockfd < 0) { fprintf(stderr, "accept function failed.\n"); exit(RT_ERR); } fprintf(stdout, "accept a new session from %s port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); event.data.fd = connsockfd; event.events = EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, connsockfd, &event); } else if(events[fd].events&EPOLLIN)//如果是已连接用户,有数据接收 { struct sockaddr_in connaddr; int connlen; if((connsockfd = events[fd].data.fd) < 0) continue; if((n = recv(connsockfd, readbuf, sizeof(readbuf), 0)) < 0) { if (errno == ECONNRESET) { close(connsockfd); events[fd].data.fd = -1; } else fprintf(stderr, "recv function failed.\n"); } else if(n == 0) { close(connsockfd); events[fd].data.fd = -1; } fprintf(stdout, "mesage: %s", readbuf); event.data.fd = connsockfd; event.events = EPOLLOUT|EPOLLET; } else if(events[fd].events&EPOLLOUT)//有数据发送 { connsockfd = events[fd].data.fd; send(connsockfd, readbuf, sizeof(readbuf), 0); event.data.fd = connsockfd; event.events=EPOLLIN|EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, connsockfd, &event); } }//end of for }//end of while return 0;}
client.c:
#include#include #include #include #include #include #include #include #include #include #define SERVERIP "192.168.0.200"#define PORT 8888#define BUFFER_SIZE 512 int main(int argc, char *argv[]){ int sockfd; struct sockaddr_in server; char sendbuf[BUFFER_SIZE]; bzero(&sendbuf,sizeof(sendbuf)); sockfd = socket(AF_INET,SOCK_STREAM,0); bzero(&server,sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = inet_addr(SERVERIP); if(connect(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr)) < 0) { perror("connect failed.\n"); return -1; } while(1) { fgets(sendbuf, sizeof(sendbuf), stdin); send(sockfd, sendbuf, sizeof(sendbuf), 0); bzero(&sendbuf,sizeof(sendbuf)); } close(sockfd); return 0;}