无法连接到internet怎么解决 无法连接到internet( 二 )


当然还有个聪明的办法,我们可以每 accept 一个客户端连接后,将这个文件描述符(connfd)放到一个数组里 。
fdlist.add(connfd);
然后弄一个新的线程去不断遍历这个数组,调用每一个元素的非阻塞 read 方法 。
while(1) {
  for(fd <– fdlist) {
    if(read(fd) != -1) {
      doSomeThing();
    }
  }
}
这样,我们就成功用一个线程处理了多个客户端连接 。
你是不是觉得这有些多路复用的意思?
但这和我们用多线程去将阻塞 IO 改造成看起来是非阻塞 IO 一样,这种遍历方式也只是我们用户自己想出的小把戏,每次遍历遇到 read 返回 -1 时仍然是一次浪费资源的系统调用 。
在 while 循环里做系统调用,就好比你做分布式项目时在 while 里做 rpc 请求一样,是不划算的 。
所以,还是得恳请操作系统老大,提供给我们一个有这样效果的函数,我们将一批文件描述符通过一次系统调用传给内核,由内核层去遍历,才能真正解决这个问题 。
select
select 是操作系统提供的系统调用函数,通过它,我们可以把一个文件描述符的数组发给操作系统,让操作系统去遍历,确定哪个文件描述符可以读写,然后告诉我们去处理:
select系统调用的函数定义如下 。
int select(
    int nfds,
    fd_set *readfds,
    fd_set *writefds,
    fd_set *exceptfds,
    struct timeval *timeout);
// nfds:监控的文件描述符集里最大文件描述符加1
// readfds:监控有读数据到达文件描述符集合,传入传出参数
// writefds:监控写数据到达文件描述符集合,传入传出参数
// exceptfds:监控异常发生达文件描述符集合, 传入传出参数
// timeout:定时阻塞监控时间,3种情况
//  1.NULL,永远等下去
//  2.设置timeval,等待固定时间
//  3.设置timeval里时间均为0,检查描述字后立即返回,轮询
服务端代码,这样来写 。
首先一个线程不断接受客户端连接,并把 socket 文件描述符放到一个 list 里 。
while(1) {
  connfd = accept(listenfd);
  fcntl(connfd, F_SETFL, O_NONBLOCK);
  fdlist.add(connfd);
}
然后,另一个线程不再自己遍历,而是调用 select,将这批文件描述符 list 交给操作系统去遍历 。
while(1) {
  // 把一堆文件描述符 list 传给 select 函数
  // 有已就绪的文件描述符就返回,nready 表示有多少个就绪的
  nready = select(list);
  …
}
不过,当 select 函数返回后,用户依然需要遍历刚刚提交给操作系统的 list 。
只不过,操作系统会将准备就绪的文件描述符做上标识,用户层将不会再有无意义的系统调用开销 。
while(1) {
  nready = select(list);
  // 用户层依然要遍历,只不过少了很多无效的系统调用
  for(fd <– fdlist) {
    if(fd != -1) {
      // 只读已就绪的文件描述符
      read(fd, buf);
      // 总共只有 nready 个已就绪描述符,不用过多遍历

推荐阅读