epoll解析 -LT ET

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字符。

 

堅持

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章