c/c++后台开发学习笔记 2.1.1 网络io与select,poll,epoll

五种IO模型

  1. 阻塞IO
  2. 非阻塞IO
    • 在系统未准备好时立即返回
    • 需要轮询
    • 在从内核读取数据到用户空间(或写入内核)时任然会阻塞
    • fcntl(fd, F_SETFL, O_NONBLOCK)
  3. 多路复用IO
    • select,poll,epoll
    • 事件驱动
  4. 异步IO
    • 告诉系统要IO,系统完成IO后通过信号告诉用户
    • 在数据IO的期间是非阻塞的
  5. 信号驱动IO
    • 用sigaction注册SIGIO的回调函数
    • 在等待数据到达的期间是非阻塞的
    • 数据就绪时,系统用SIGIO通知用户,触发回调函数
    • 回调函数调用read读取(阻塞IO)

多线程模型

每个线程要8M的栈(可以调整),极限是C10K

while (1) {

		struct sockaddr_in client;
	    socklen_t len = sizeof(client);
	    connfd = Accept(listenfd, (struct sockaddr *)&client, &len)
		pthread_t threadid;
		pthread_create(&threadid, NULL, client_routine, (void*)&connfd);
    }

select

每次要轮询每个fd看有没有事件,有什么事件(读、写、异常)
不能超过1024个fd,可以调整,但是要改源码重新编译,很麻烦
每个线程/进程一个select,可以到达C1000K(一百万)
因为select需要复制fd set,效率较慢,fd多了就太慢

    fd_set rfds, rset, wfds, wset;
	FD_ZERO(&rfds);
	FD_SET(listenfd, &rfds);
	FD_ZERO(&wfds);
	int max_fd = listenfd;

	while (1) {
		rset = rfds;
		wset = wfds;
		int nready = select(max_fd+1, &rset, &wset, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) { //
			struct sockaddr_in client;
		    socklen_t len = sizeof(client);
		    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
		        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
		        return 0;
		    }
			FD_SET(connfd, &rfds);
			if (connfd > max_fd) max_fd = connfd;
			if (--nready == 0) continue;
		}

		int i = 0;
		for (i = listenfd+1;i <= max_fd;i ++) {
			if (FD_ISSET(i, &rset)) { // 
				n = recv(i, buff, MAXLNE, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);
					FD_SET(i, &wfds); //reactor的写法,更标准
					//send(i, buff, n, 0); //也可以,简单点
		        } else if (n == 0) { //EOF
					FD_CLR(i, &rfds); //一定要去除了,否则会重复触发事件
		            close(i);
		        }
				if (--nready == 0) break;
			} else if (FD_ISSET(i, &wset)) {
				send(i, buff, n, 0);
				FD_CLR(i, &wfds);
				FD_SET(i, &rfds);
			}
		}
	}

poll

不受1024fd的限制,不过原理和select一样,只是接口改变了一下

struct pollfd fds[POLL_SIZE] = {0};
	fds[0].fd = listenfd;
	fds[0].events = POLLIN;

	int max_fd = listenfd;
	int i;
	for (i = 1;i < POLL_SIZE;i ++) {
		fds[i].fd = -1;
	}
	while (1) {
		int nready = poll(fds, max_fd+1, -1); //-1是timeout
		if (fds[0].revents & POLLIN) {
			struct sockaddr_in client;
		    socklen_t len = sizeof(client);
		    if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) {
		        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
		        return 0;
		    }
			printf("accept \n");
			fds[connfd].fd = connfd; //也不一定要在下标为connfd的地方存放
			fds[connfd].events = POLLIN;

			if (connfd > max_fd) max_fd = connfd;
			if (--nready == 0) continue;
		}
		for (i = listenfd+1;i <= max_fd;i ++)  {
			if (fds[i].revents & POLLIN) {
				n = recv(i, buff, MAXLNE, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);
					send(i, buff, n, 0);
		        } else if (n == 0) { //EOF
					fds[i].fd = -1;
		            close(i);
		        }
				if (--nready == 0) break;
			}
		}
	}

epoll

把需要监听的fd存在红黑树里,方便查询
每次返回一个队列,队列里面每个fd都有IO事件,不用每次都遍历整个需要监听的fd set

// epoll_create 
	// epoll_ctl(ADD, DEL, MOD)
	// epoll_wait

	int epfd = epoll_create(1); //int size, 底层没有用到,以前是定长数组,现在是链表,为了向前兼容

	struct epoll_event events[POLL_SIZE] = {0};//每次取event的大小,类似batch size
	struct epoll_event ev; //epoll_ctl的输入参数

	ev.events = EPOLLIN;
	ev.data.fd = listenfd;

	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

	while (1) {
		int nready = epoll_wait(epfd, events, POLL_SIZE, 5); //最后一个参数是timeout, -1则一直等待
		if (nready == -1) {
			continue; //timeout
		}
		
		int i;
		for (i = 0;i < nready; i++) {
			int clientfd =  events[i].data.fd;
			if (clientfd == listenfd) {
				struct sockaddr_in client;
			    socklen_t len = sizeof(client);
			    connfd = Accept(listenfd, (struct sockaddr *)&client, &len);
				ev.events = EPOLLIN;
				ev.data.fd = connfd;
				epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); //新fd添加到监听的集合中
			} else if (events[i].events & EPOLLIN) {
				n = recv(clientfd, buff, MAXLNE, 0);
		        if (n > 0) {
		            buff[n] = '\0';
		            printf("recv msg from client: %s\n", buff);
					send(clientfd, buff, n, 0);
		        } else if (n == 0) { //EOF
					ev.events = EPOLLIN;
					ev.data.fd = clientfd;
					epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, &ev);
		            close(clientfd);
		        }
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值