无法连接到internet怎么解决 无法连接到internet


无法连接到internet怎么解决 无法连接到internet


为了讲多路复用,当然还是要跟风,采用鞭尸的思路,先讲讲传统的网络 IO 的弊端,用拉踩的方式捧起多路复用 IO 的优势 。
为了方便理解,以下所有代码都是伪代码,知道其表达的意思即可 。
Let's go
阻塞 IO
服务端为了处理客户端的连接和请求的数据,写了如下代码 。
listenfd = socket();   // 打开一个网络通信端口
bind(listenfd);        // 绑定
listen(listenfd);      // 监听
while(1) {
  connfd = accept(listenfd);  // 阻塞建立连接
  int n = read(connfd, buf);  // 阻塞读数据
  doSomeThing(buf);  // 利用读到的数据做些什么
  close(connfd);     // 关闭连接,循环等待下一个连接
}
这段代码会执行得磕磕绊绊,就像这样 。
可以看到,服务端的线程阻塞在了两个地方,一个是 accept 函数,一个是 read 函数 。
如果再把 read 函数的细节展开,我们会发现其阻塞在了两个阶段 。
这就是传统的阻塞 IO 。
整体流程如下图 。
所以,如果这个连接的客户端一直不发数据,那么服务端线程将会一直阻塞在 read 函数上不返回,也无法接受其他客户端连接 。
这肯定是不行的 。
非阻塞 IO
为了解决上面的问题,其关键在于改造这个 read 函数 。
有一种聪明的办法是,每次都创建一个新的进程或线程,去调用 read 函数,并做业务处理 。
while(1) {
  connfd = accept(listenfd);  // 阻塞建立连接
  pthread_create(doWork);  // 创建一个新的线程
}
void doWork() {
  int n = read(connfd, buf);  // 阻塞读数据
  doSomeThing(buf);  // 利用读到的数据做些什么
  close(connfd);     // 关闭连接,循环等待下一个连接
}
这样,当给一个客户端建立好连接后,就可以立刻等待新的客户端连接,而不用阻塞在原客户端的 read 请求上 。
不过,这不叫非阻塞 IO,只不过用了多线程的手段使得主线程没有卡在 read 函数上不往下走罢了 。操作系统为我们提供的 read 函数仍然是阻塞的 。
所以真正的非阻塞 IO,不能是通过我们用户层的小把戏,而是要恳请操作系统为我们提供一个非阻塞的 read 函数 。
这个 read 函数的效果是,如果没有数据到达时(到达网卡并拷贝到了内核缓冲区),立刻返回一个错误值(-1),而不是阻塞地等待 。
操作系统提供了这样的功能,只需要在调用 read 前,将文件描述符设置为非阻塞即可 。
fcntl(connfd, F_SETFL, O_NONBLOCK);
int n = read(connfd, buffer) != SUCCESS);
这样,就需要用户线程循环调用 read,直到返回值不为 -1,再开始处理业务 。
这里我们注意到一个细节 。
非阻塞的 read,指的是在数据到达前,即数据还未到达网卡,或者到达网卡但还没有拷贝到内核缓冲区之前,这个阶段是非阻塞的 。
当数据已到达内核缓冲区,此时调用 read 函数仍然是阻塞的,需要等待数据从内核缓冲区拷贝到用户缓冲区,才能返回 。
整体流程如下图
IO 多路复用
为每个客户端创建一个线程,服务器端的线程资源很容易被耗光 。

推荐阅读