网络编程学习之Select

1. 基本概念

我们在上一篇文章代码的注释中讲到了关于C10K的问题,也就是同时10k个客户端来访问服务器,如果采用pthread_create()函数来对每一个服务器的访问创建一个新的线程来处理,虽然说同一个进程下的上下文的切换不是很大, 但是频繁地创建和销毁线程就会有很大的系统开销。即使我们使用线程池来避免线程的创建和销毁,10k个线程也是操作系统无法承受的量级了。因此我们引入了三种方式来处理该问题,分别是select, poll和epoll。他们的核心原理都是基于IO多路复用技术,因为我们已经知道了无法通过多个进程或者线程来处理,那么我们就需要通过通过一个进程或者线程来实现,也就是“复用”,多个客户端请求重复使用同一个进程/线程。

select实现IO多路复用的方式是来维护一个文件描述符(File Descriptor)集合,然后调用select函数将该文件描述符集合拷贝到内核中(select函数见下文代码分析),然后我们通过遍历来检测是否有事件产生,如果有事件产生,我们则将整个文件描述符集合在copy到用户的程序中,用户的程序再遍历进行检测到具体是哪一个事件产生,之后进行处理。

文件描述符的集合使用bitmap来维护,也就是fds_bits,默认最大值是1024。

Select缺点:

  1. 参数较多不利于维护(5个)
  2. 每次都需要将所有的rset集合(待检测的io集合)拷贝到内核,再从内核拷贝出来
  3. 对io数量有限制,收到文件描述符最大数量的限制

2. 代码分析

//select 单线程处理多个客户端
	//0:stdin 1:stdout 2:stderr 3:listen 
	//int nready = select(maxfd, rset, wset, eset, timeout); //判断最大fd的值, 可读, 可写, 出错, 轮循间隔
	/*typedef struct {
		unsigned long fds_bits[1024 / (8 * sizeof(long))];
	} __kernel_fd_set;*/
	
	fd_set rfds, rset;
	//清除文件描述符集合中的所有位,即初始化 rfds 集合为空
	FD_ZERO(&rfds);
	//将监听套接字文件描述符sockfd添加到rfds中
	FD_SET(sockfd, &rfds);
	//初始化,后文会修改
	int maxfd = sockfd;
	
	printf("loop\n");
	
	while (1) {
		rset = rfds;
		
		int nready = select(maxfd + 1, &rset, NULL, NULL, NULL/*一直等待*/);
		//检测服务端是否连接,开始监听
		if (FD_ISSET(sockfd, &rset)) {
			struct sockaddr_in clientaddr;
			socklen_t len = sizeof(clientaddr);
			//接受新的连接请求
			int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
			//输出哪个socket在处理
			printf("sockfd: %d\n", sockfd);
			//将客户端fd添加至rfds,为后续遍历找到该客户端准备
			FD_SET(clientfd, &rfds);
			
			maxfd = clientfd;//maxfd = max(maxfd, clientfd)
		}
		int i = 0;
        //遍历所有的文件描述符集合
		for (i = sockfd + 1; i <= maxfd; i++) {
            //判断i对应的fd是否有事件发生
			if (FD_ISSET(i, &rset)) {
				char buffer[128] = {0};
				int count = recv(i, buffer, 128, 0);
				if (count == 0) { //调用close()
					printf("disconnect\n");
					//close(i); ①
					//FD_CLR(i, &rfds); ②
					//标准断开:清空多路io复用再关闭 ③
					FD_CLR(i, &rfds);//第三种才是正确的方式
					close(i);
					
					break;
			   }
				send(i, buffer, count/*128*/, 0);//发送至客户端检测整个过程是否正确
				printf("sockfd: %d, clientfd: %d, count: %d, buffer: %s\n", sockfd, i, count, buffer);
			}
		}
	}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值