Linux服务器6 --- EPOLL模型函数接口,服务端代码,工作流程及其优缺点

一、EPOLL的函数接口
1、监听树节点

#include <sys/epoll.h>
/* 监听树节点 */
struct epoll_event
{
   events = //设置监听事件 EPOLLIN |EPOLLOUT
   struct data
   {
      int sockfd //监听的sockfd
   }
}

2、监听集合

/*创建epoll监听集合[红黑树] , 成功返回树文件描述符*/
int epfd = epoll_create(int max); //epoll监听数量

3、监听树的增删改查

epoll_ctl(int epfd , int cmd , int sockfd , struct epoll_event * env);
参数介绍:
epfd = 监听树的文件描述符
cmd = EPOLL_CTL_ADD [监听树节点添加] EPOLL_CTL_DEL [监听树节点删除] EPOLL_CTL_MOD [监听树节点修改]
sockfd = 与监听树中某个监听节点对应
env = 监听树节点,用于设置监听

4、Epoll模型的监听函数

int readycode = epoll_wait(int epfd , struct epoll_event * readyarr, int maxevents , int timeout);
参数介绍:
epfd = 设置监听树
readyarray = 就绪队列,有事件就绪,内核传出就绪的节点到此队列中(遍历处理即可) , 就绪队列大小一般与最大监听数一致
maxevents = 最大就绪数 ,一般与最大监听数一致
timeout = 等待超时, 可以根据需求改变epoll工作模式
timeout = -1 阻塞等待监听
timeout = 0 非阻塞监听
timeout > 0 定时阻塞

二、EPOLL模型服务端代码

#include<SOCKET_API.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8000
#define BUFSIZE 1500
#define TIMEOUT 1
#define BACKLOG 128
#define SETSIZE 1024
#define EPOLLSIZE 200000

int main()
{
	//网络初始化
	struct sockaddr_in serveraddr,clientaddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(SERVER_PORT);
	inet_pton(AF_INET,SERVER_IP,&serveraddr.sin_addr.s_addr);
	int serverfd=SOCKET(AF_INET,SOCK_STREAM,0);
	BIND(serverfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
	LISTEN(serverfd,BACKLOG);

	//业务处理数据
	int recvsize;
	char buffer[BUFSIZE];
	bzero(buffer,sizeof(buffer));
	int flags;
	int clientfd;
	socklen_t addrlen;

	//epoll初始化
	int readycode;
	int epfd;
	struct epoll_event listen_set[EPOLLSIZE]; //epoll就绪队列

	epfd = epoll_create(EPOLLSIZE); //创建监听树

	struct epoll_event lnode; //监听节点
	lnode.events = EPOLLIN;
	lnode.data.fd = serverfd;//设置监听节点

	//添加监听节点
	epoll_ctl(epfd,EPOLL_CTL_ADD,serverfd,&lnode);

	printf("epoll server Waiting..\n");
	while(TIMEOUT)
	{
		readycode = epoll_wait(epfd,listen_set,EPOLLSIZE,-1);
		while(readycode)//循环处理就绪
		{
			if(listen_set[readycode-1].data.fd == serverfd)
			{
				//serverfd就绪?建立连接
				addrlen = sizeof(clientaddr);
				clientfd = ACCEPT(serverfd,(struct sockaddr*)&clientaddr,&addrlen);
				//对clientfd设置监听
				lnode.data.fd = clientfd;
				lnode.events = EPOLLIN;
				epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&lnode);
			}
			else
			{
				//clientfd就绪?处理业务
				if((recvsize=RECV(listen_set[readycode-1].data.fd,buffer,sizeof(buffer),0))>0)
				{
					printf("epoll server recv request size = %d\n",recvsize);
					flags = 0;
					while(flags < recvsize)
					{
						buffer[flags] = toupper(buffer[flags]);
						flags++;
					}
					int sendsize = SEND(listen_set[readycode-1].data.fd,buffer,recvsize,0);
					printf("epoll server send response size = %d\n",sendsize);
					bzero(buffer,sizeof(buffer));

				}
				else if(recvsize == 0)//客户端退出
				{
					close(listen_set[readycode-1].data.fd);
					epoll_ctl(epfd,EPOLL_CTL_DEL,listen_set[readycode].data.fd,NULL);//delete 
				}
			
			}
			readycode--;
		}

	}
	return 0;
}

请添加图片描述
在这里插入图片描述
三、EPOLL的工作流程
当我们有了监听树以后,监听树上有监听节点,每一个监听节点除了有指定的sockfd和设置的监听事件以外,还有一个ep_callback()回调函数,(select poll epoll)监听网络IO的,网络IO到底是谁来监听,别人发数据,谁知道网络事件就绪,网络设备,网卡或者是驱动层接口,epoll摒弃了传统的IO设备等待队列,直接用监听节点与网络设备绑定 ,每当向监听树上添加一个节点,这个节点的ep_callback()就会被注册到网络设备上的驱动层,sockfd与网络设备绑定 ,将节点中的回调函数注册到网络设备中。假设现在有某个节点的ep_callback()已经注册到网络设备上了,某一时刻节点产生就绪事件,网络设备监听到就会调用这个节点对应的回调函数,将就绪事件传出到就绪链表(双向链表)中(内核层的就绪链表,而非用户层的就绪队列)。就绪事件epitem类型,是一个结构体类型,我们可以简单理解为由监听节点和其他部分组成,某个节点就绪就通过回调函数将这个节点传出到就绪链表中。epoll模型不需要轮询,只需要检测就绪链表是否为空即可,为空就睡眠等待,不为空就返回就绪。就绪链表与就绪队列也有一层关系,需要将就绪链表中的内容拷贝一份到就绪队列中,拷贝过程中会有开销,为了使开销更小,效率更高,利用MMAP将内核层的就绪事件传到用户层,内存共享映射,而不是传统的copy拷贝。
四、EOLL模型的优缺点
1、优点
1)采用的是网络设备绑定+回调机制监听反馈事件,不需要轮询监听,效率高开销小,没有监听数量限制 ,可以实现百万级监听(监听能力出众)。
2)当epoll模型监听到sockfd事件就绪,可以直接传出就绪的sockfd到就绪队列,用户只需要遍历就绪队列依次处理就绪即可(使用方便)。
3)相比SELECT与POLL,EPOLL业务处理更及时,其一不需要轮询监听(延迟) ,其二不需要查找就绪(延迟) ,大大提高业务处理的实时性。
4)EPOLL的监听树是在内核空间创建的, 用户使用时只需要将准备的节点,拷贝挂载的内核监听树中即可, 而且监听树内部有去重机制, 最大限度的保证每个节点只拷贝一次并且只挂载一次(减少不必要的系统开销)。
5)EPOLL兼容性比较好, LINUX与UNIX平台兼容出色
2、弊端
EPOLL监听核心是红黑树(近似平衡二叉树),如果对监听集合访问较频繁,会导致系统开销增大 (维护监听树的成本较高)。
五、单进程IO复用模型
1)单进程模型中, 客户端的请求业务不能过于复杂, 如果过于复杂导致服务端进程处理时间过长(服务端其他业务停摆)。
2)如果业务处理简单(ms,us内可以处理完毕) 秒内也可以处理大量客户端业务。
3)单进程模型需要对业务复杂度进行限制,还要对业务量进行限制。(不能很好提供服务)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值