3.同时管理多个socket的简单方法-select处理

本篇是第三篇,主要用来讲解作为服务器的机器是如何管理多个socket的客户端连接的,毕竟recv只能监视单个socket。

一、背景介绍

在此之前,我们先来看下"操作系统是如何区分网络收到的数据是属于那一个socket的?"

答案:socket与端口号是一一对应的,操作系统会维护端口号到socket的索引结构,以快速读取,所以操作系统可以很方便的找到收到的网络数据属于那一个socket。

基于前面第2篇的知识,如果我们能够做到传递一个socket的列表,并且能够做到在socket列表没有数据的时候挂起进程,只要有一个socket有数据就唤醒这个进程貌似就可以解决这个问题。而这个也恰恰就是select的实现思路。

二、select介绍

我们通过使用select的代码来分析select的过程

int s = socket(AF_INET, SOCK_STREAM, 0);  
bind(s, ...)
listen(s, ...)


int fds[] =  // 用于存放需要监听的socket


while(1){ // 死循环,利用操作系统的进程阻塞和唤醒来工作
    int n = select(..., fds, ...) // 传入fds
    for(int i=0; i < fds.count; i++){ // 唤醒进程之后,遍历所有的socket
        if(FD_ISSET(fds[i], ...)){ // 利用FD_ISSET来判断对应的socket是否有数据
            // fds[i]的数据处理
        }
    }

1.调用select之后,操作系统把进程A分别加入这三个socket的等待队列中。

2.当任何一个socket收到数据后,中断程序将唤起进程。下图展示了sock2接收到了数据的处理流程。

3.所谓唤起进程,就是将进程从所有的等待队列中移除,加入到工作队列里面。

当进程A被唤醒后,它知道至少有一个socket接收了数据。程序只需遍历一遍socket列表,就可以得到就绪的socket。

三、select的不足之处

其一,每次调用select都需要将进程加入到所有监视socket的等待队列,每次唤醒都需要从每个队列中移除。这里涉及了两次遍历,而且每次都要将整个fds列表传递给内核,有一定的开销。正是因为遍历操作开销大,出于效率的考量,才会规定select的最大监视数量,默认只能监视1024个socket。

其二,进程被唤醒后,程序并不知道哪些socket收到数据,还需要遍历一次。

补充说明:本节只解释了select的一种情形。当程序调用select时,内核会先遍历一遍socket,如果有一个以上的socket接收缓冲区有数据,那么select直接返回,不会阻塞。这也是为什么select的返回值有可能大于1的原因之一。如果没有socket有数据,进程才会阻塞。


参考文档:

https://zhuanlan.zhihu.com/p/64138532

 

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值