Linux网络编程【高并发服务器03_多路IO转接_epoll】

目录

epoll函数

        epoll_create、epoll_ctl、epoll_wait

int epoll_create(int size);          创建一颗监听红黑树

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);        操作监听红黑树

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

                阻塞监听

epoll模型图

epoll 代码实现

epoll事件模型


        epoll是Linux下多路复用IO接口的 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被监听的文件描述符集合,零一点原因就是获取事件的时候,无需遍历整个被监听的描述符集合,只要遍历那些被内核IO事件异步唤醒儿加入Ready队列中的描述符集合就行了

epoll函数

        epoll_create、epoll_ctl、epoll_wait

int epoll_create(int size);          创建一颗监听红黑树

        参数:

                size:创建的红黑树的监听节点数量。(仅供内核参考)

        返回值:

指向新创建的红黑树的根节点fd

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);        操作监听红黑树

        参数:

                epfd:epoll_create(); 函数的返回值

                op:对该监听红黑树所做的操作

                        EPOLL_CTL_ADD        添加fd到 监听红黑树

                        EPOLL_CTL_MOD        修改fd在 监听红黑树上的监听事件

                        EPOLL_CTL_DEL        将一个fd 从监听红黑树上摘除

                fd:待监听的 fd

                event:本质 struct epoll_event 结构体地址

                typedef union epoll_data {
                             void   *ptr;
                             int     fd;                /*对应监听事件的红黑树*/
                             uint32_t     u32;
                             uint64_t     u64;
                } epoll_data_t;

               struct epoll_event {
                             uint32_t   events;      /* EPOLLIN、EPOLLOUT、EPOLLERR*/
                             epoll_data_t data;        /* User data variable */
               };

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

                阻塞监听

        参数:

                epfd:epoll_create(); 函数的返回值

                events:传出参数【数组】,满足监听条件的 那些fd结构体

                maxevents:数组元素的 总个数

                        struct epoll_events[1024] 

                timeout:        -1:阻塞

                                        0:不阻塞

                                      >0:超时时间

        返回值:

                >0:满足坚挺的总个数。可以用做循环上限。

epoll模型图

         

epoll 实现多路IO转接思路


lfd = socket();
bind();
listen();


int epfd = epoll_create();        //设置红黑树结点

struct epoll_event     tmp, ep[1024];    //tmp:用来设置单个fd属性 ep:epoll_wait函数返回值

tmp.events = EPOLLIN;
tmp.data.fd = lfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &tmp);    //将lfd挂载到红黑树

while(1) {
    ret = epoll_wait(epfd, ep, 1024, -1);    //实时监听
    for(i = 0; i < ret; i++) {
        if(ep[i].data.fd == lfd) {    //lfd满足读事件,有新的客户端要发生连接请求
            cfd = accept();

            tmp.events = EPOLLIN;     //初始化cfd的监听属性
            emp.data.fd = cfd;

            epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp);    //将cfd挂载到树上
        } else if {    //cfd发生数据请求
            int n = read(ep[i].data.fd, buf, sizeof(buf));
            if( n == 0 ) {
                close(ep[i].data.fd);
                epoll_ctl(epfd, EPOLL_CTL_DEL, ep[i].data.fd, NULL);
            } else if( n > 0) {
                //数据交换
        }

    }
}

epoll 代码实现

#define MAXLINE 8192
#define SERV_PORT 8000
#define OPEN_MAX 5000

int main(int arg, char *argv[])
{
	int i, listenfd, connfd, sockfd;
	int n, num = 0;
	sszie_t nready, efd, res;
	char buf[MAXLINE], str [INET_ADDRSTRLEN];
	socklen_t clien;
	struct sockaddr_in client_addr, serv_addr;
	struct epoll_envent tep, ep[OPEN_MAX];
	
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	
	int opt = 1;
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	
	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	listen(listenfd, 128);
	
	efd = epoll_create(OPEN_MAX);
	if(efd == -1)
		perror("epoll_create error");

	tep.events = EPOLLIN;
	tep.data.fd = listenfd;
	res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);

	while(1) {
		nready = epoll_wait(efd, ep, OPEN_MAX, -1);
		
		for(i = 0; i < nready; i++) {
			if(!(ep[i].events & EPOLLIN))
				continue;
				
			if(ep[i].data.fd == listenfd) {
				clien = sizeof(client_addr);
				connfd = accept(listenfd, (struct sockaddr *)&client_addr, &clien);
				printf("cfd:%d==client %d\n", connfd, ++num);
				
				tep.events = EPOLLIN;
				tep.data.fd = connfd;
				res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
			} else {
				sockfd = ep[i].data.fd;
				n = read(sockfd, buf, MAXLINE);
				if(n == 0) {
					res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
					if(res == -1)
						perror("epoll_ctl error");
					close(sockfd);
				}else {
					for(i = 0; i < n; i++) 
						buf[i] = toupper(buf[i]);
					
					write(STDOUT_FILENO, buf, n);
					write(sockfd, buf, n);
				}

			}
		}
	}
}

epoll事件模型

        EPOLL时间有两种模型

                ET:边沿触发

                        缓冲区剩余未读尽的数据不会导致epoll_wait返回。新的读/写时间满足才会触发

                LT:水平触发        ==默认采用模式, 支持block和no-block

                        缓冲区剩余未读尽的数据会导致epoll_wait 函数返回

        结论:epoll的ET模式是高效模式,但是只支持非阻塞模式,可以通过fcntl改变文件属性

        优点:高效。能突破文件描述符上限

        缺点:不能跨平台。只能Linux

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值