I/O多路复用epoll函数
epoll是通过一组函数来完成多路复用的
其中epoll_create、epoll_ctl、epoll_wait函数,不懂的可以查阅资料!
这篇博客主要记录 epoll的LT模式和ET模式的区别
LT模式:epoll相当于一个效率较高的poll, 当描述符有事件发生时,会通知用户读取描述符的事件(消息), 当事件没有一次读取完还会继续通知用户去读事件(消息)
ET模式:往epoll内核事件表中的文件描述符上注册EPOLLET事件时,epoll将以ET的模式来操作该文件描述符,ET模式是高效的工作模式。
LT工作模式,epoll_wait返回就绪文件描述符时,通知应用程序,应用程序可以不立即处理该事件,下次调用epoll_wait时,epoll_wait还会通知应用程序该描述符有就绪的事件。
LT工作模式,epoll_wait返回就绪文件描述符时,通知应用程序,应用程序必须立即处理该事件,如果没处理事件或者消息没读完 下次调用epoll_wait时,epoll_wait就不会通知应用程序该描述符这一事件了, 除了有该描述符有新的事件继续到来,epoll_wait通知应用程序该描述符有事件到来。
先看epoll LT模式的服务器:
# include<stdio.h>
# include<sys/epoll.h>
# include<sys/socket.h>
# include<assert.h>
# include<arpa/inet.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<stdlib.h>
# include<error.h>
#define MAXFD 10
void epoll_add(int epfd, int fd)
{
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = fd;
if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
{
perror("epoll ctl error");
}
}
void epoll_del(int epfd, int fd)
{
if(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1)
{
perror("epoll ctl del error\n");
}
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in saddr, caddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
assert(res != -1);
listen(sockfd, 5);
int epfd = epoll_create(MAXFD);
epoll_add(epfd, sockfd);
struct epoll_event events[MAXFD];
while(1)
{
printf("epoll_wait begin: \n");
int n = epoll_wait(epfd, events, MAXFD, 5000);
if(n == -1)
{
printf("epoll_wait error\n");
continue;
}
else if(n == 0)
{
printf("time out\n");
continue;
}
else
{
int i = 0;
for(; i < n; i++)
{
int fd = events[i].data.fd;
if(events[i].events & EPOLLIN)
{
if(fd == sockfd)
{
int len = sizeof(caddr);
int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
if( c < 0)
{
continue;
}
printf("accept c = %d\n", c);
epoll_add(epfd, c);
}
else
{
char buff[128] = {0};
int num = recv(fd, buff, 1, 0);
if(num <= 0)
{
epoll_del(epfd, fd);
close(fd);
printf("one client over\n");
continue;
}
else
{
printf("recv(%d) = %s\n",fd, buff);
send(fd, "ok", 2, 0);
}
}
}
}
}
}
}
当有客户端连接发送消息的时候
客户端发送hello, 每次只recv一字节数据,会epoll_wait通知5次。
epoll ET模式的服务器:
# include<stdio.h>
# include<sys/epoll.h>
# include<sys/socket.h>
# include<assert.h>
# include<arpa/inet.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<stdlib.h>
# include<errno.h>
# include<fcntl.h>
#define MAXFD 10
void setnonblock(int fd)
{
int oldfl = fcntl(fd, F_GETFL);
int newfl = oldfl | O_NONBLOCK;
if(fcntl(fd, F_SETFL, newfl) == -1)
{
perror("fcntl error\n");
}
}
void epoll_add(int epfd, int fd)
{
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = fd;
if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
{
perror("epoll ctl error");
}
setnonblock(fd);
}
void epoll_del(int epfd, int fd)
{
if(epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1)
{
perror("epoll ctl del error\n");
}
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
assert(sockfd != -1);
struct sockaddr_in saddr, caddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
assert(res != -1);
listen(sockfd, 5);
int epfd = epoll_create(MAXFD);
epoll_add(epfd, sockfd);
struct epoll_event events[MAXFD];
while(1)
{
printf("epoll_wait\n");
int n = epoll_wait(epfd, events, MAXFD, 5000);
if(n == -1)
{
printf("epoll_wait error\n");
continue;
}
else if(n == 0)
{
printf("time out\n");
continue;
}
else
{
int i = 0;
for(; i < n; i++)
{
int fd = events[i].data.fd;
if(events[i].events & EPOLLIN)
{
if(fd == sockfd)
{
int len = sizeof(caddr);
int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
if( c < 0)
{
continue;
}
printf("accept c = %d\n", c);
epoll_add(epfd, c);
}
else
{
// while(1)
// {
char buff[128] = {0};
int num = recv(fd, buff, 1, 0);
printf("NUM: %d\n ", num);
if(num == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
{
send(fd, "ok", 2, 0);
}
break;
}
else if(num == 0)
{
printf("del/n");
epoll_del(epfd, fd);
close(fd);
printf("one client over\n");
break;
}
printf("recv %d = %s\n",fd, buff);
send(fd, "ok", 2, 0);
// }
}
}
}
}
}
}
客户端连接服务器,第一次发送hello 第二次发送world
先读取h字符,等再次通知的时候读取l字符。
坚持