嵌入式 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;i<nfds;++i)
{
if(events[i].data.fd==listenfd) //有新的连接
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //添加fd到监视
}
else if( events[i].events&EPOLLIN ) //接收到数据,读socket
{
n = read(sockfd, line, MAXLINE)) < 0 //读
ev.dataNaNr = md; //md为自定义类型,添加数据
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识
}
else if(events[i].events&EPOLLOUT) //有数据待发送,写socket
{
struct myepoll_data* md = (myepoll_data*)events[i].dataNaNr;
sockfd = md->fd;
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 <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
#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 <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#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;
}
转载于:https://blog.51cto.com/9291927/1825226