网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

22 | 非阻塞I/O:提升性能的加速器

盛延敏 2019-09-27
你好,我是盛延敏,这里是网络编程实战第 22 讲,欢迎回来。
在性能篇的前两讲中,我分别介绍了 select 和 poll 两种不同的 I/O 多路复用技术。在接下来的这一讲中,我将带大家进入非阻塞 I/O 模式的世界。事实上,非阻塞 I/O 配合 I/O 多路复用,是高性能网络编程中的常见技术。

阻塞 VS 非阻塞

当应用程序调用阻塞 I/O 完成某个操作时,应用程序会被挂起,等待内核完成操作,感觉上应用程序像是被“阻塞”了一样。实际上,内核所做的事情是将 CPU 时间切换给其他有需要的进程,网络应用程序在这种情况下就会得不到 CPU 时间做该做的事情。
非阻塞 I/O 则不然,当应用程序调用非阻塞 I/O 完成某个操作时,内核立即返回,不会把 CPU 时间切换给其他进程,应用程序在返回后,可以得到足够的 CPU 时间继续完成其他事情。
如果拿去书店买书举例子,阻塞 I/O 对应什么场景呢? 你去了书店,告诉老板(内核)你想要某本书,然后你就一直在那里等着,直到书店老板翻箱倒柜找到你想要的书,有可能还要帮你联系全城其它分店。注意,这个过程中你一直滞留在书店等待老板的回复,好像在书店老板这里"阻塞"住了。
那么非阻塞 I/O 呢?你去了书店,问老板有没你心仪的那本书,老板查了下电脑,告诉你没有,你就悻悻离开了。一周以后,你又来这个书店,再问这个老板,老板一查,有了,于是你买了这本书。注意,这个过程中,你没有被阻塞,而是在不断轮询。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(19)

  • MoonGod
    感觉这篇的解释和前面的比起来太不细致了…很多地方都没说明。老师能不能多一些说明啊

    作者回复: 我在代码里多加一些注释,可以看最新的代码

    https://github.com/froghui/yolanda

    2019-09-27
    3
  • 沉淀的梦想
    老师那个accept阻塞的实验,在我电脑(linux-4.18.0,ubuntu18.10)上的行为有点不太一样,无论是把listen_fd设置为阻塞还是非阻塞,sleep 5s还是10s或者更长,行为总是:accept成功获取到客户端连接,然后读取到客户端的RST

    作者回复: 这里举的一个例子,有可能在新的协议栈下有所改变,但是不管怎样,理论上的分析有可能导致阻塞,所以我们还是应该在平时编码中将监听套接字也设置为非阻塞的。

    2019-09-30
    2
    2
  • yusuf
    1、133行判断是否超过了文件描述符的最大值,如果超过了,就会报错。可以考虑使用动态分配的方式,但如果超过了1024的话,使用上一节中的poll来处理会更好些
    2、认为是考虑到对同一个fd的同一缓冲区进行读写操作,只用一个Buffer对象足够了
    2019-09-29
    2
  • 一天到晚游泳的鱼
    老师,我想请教一个问题就是, 当把一个描述符设置为非阻塞的之后,在该描述符上面的操作就会变成非阻塞的吗? 比如说把连接套接字设置为非阻塞的,send和recv就会变成非阻塞的吗?

    作者回复: 你的理解是对的。

    2019-10-05
    1
  • 石将从
    老师代码能不能写多点注释呀?基础差的同学看得费劲

    作者回复: 好的,我在github上的提交多加一些注释。

    https://github.com/froghui/yolanda

    2019-09-28
    1
    1
  • godtrue
    非阻塞 I/O 可以被用到读操作、写操作、接收连接操作和发起连接操作上。
    非阻塞是相对于阻塞而言的,阻塞简单讲就是应用程序让内核做某一件事,内核需要点时间做,应用程序在内核做事的这段时间里啥也不干就干等着,直到内核把这件事干完,然后告诉应用程序结果,应用程序才接着干别的事情。
    非阻塞的关键点是,不干等着了,在内核做事的那个时间段内,继续干自己能的事,等内核干完了应用程序交给它干的事,把结果返给应用程序时,应用程序则再继续干拿到结果之后应干的事。
    2019-11-24
  • 贺荣伟
    这一章需要反复阅读和理解
    2019-11-21
  • zmysang
    请问老师,我理解select函数是,如果readset中的fd的接收缓冲区不为空,就是可读的,如果writeset中的fd的发送缓冲区没有满就是可写的是吗?

    作者回复: 可以这么理解。当然,接收缓冲去可读的大小,和发送缓冲区可写的大小都是可以通过内核参数动态配置。

    2019-11-18
  • javaYJL
    老师,accept()这个函数不是阻塞的吗?

    作者回复: accept和阻塞套接字一起使用就是阻塞的,和非阻塞套集字一起使用就是非阻塞的。阻塞和非阻塞是作用到套集字上的。

    2019-11-05
  • JasonZhi
    第一段代码的注释是否有误?EINTR应该属于中断导致的错误,如:信号中断,如果是非阻塞没有数据可读的情况下,应该返回错误EAGAIN

    作者回复: 你是对的,我已经修正代码,等待编辑更新,感谢指正。

    2019-10-17
  • Geek_d4f974
    这章真的晦涩难懂啊

    作者回复: 还好吧,这里主要是说非阻塞I/O的含义,以及对我们编写程序的帮助,多读一下,应该可以搞定的。

    2019-10-11
  • 传说中的成大大
    有连接上来的时候sleep5秒那个例子中 我这边没有永久阻塞 反而是 ./server: accept failed: Invalid argument

    作者回复: 这个例子就是说明一下监听套接字需要设置为非阻塞的,结果不重要了。

    2019-10-08
  • 刘晓林
    老师,有一个地方不是很明白。在连接套接字设为阻塞时,当客户端发送RST后,服务端在已完成连接队列删除了连接,accept阻塞。这时候如果有新连接进来了,为什么accept还是会阻塞呀?难道新连接进来一定要先select之后,accept才能取到连接好的套接字??

    作者回复: 不是一定需要select才可以 accept的。这里的例子主要是说明如果不给监听套集字设置为非阻塞,可能会引起的问题。

    2019-10-04
  • 沉淀的梦想
    老师代码中进行rot13_char编码的目的是啥?

    作者回复: ROT13(回转13位,rotateby13places,有时中间加了个减号称作ROT-13)是一种简易的置换暗码。

    ROT-13 编码是一种每一个字母被另一个字母代替的方法。这个代替字母是由原来的字母向前移动 13 个字母而得到的。数字和非字母字符保持不变。

    它是一种在网路论坛用作隐藏八卦、妙句、谜题解答以及某些脏话的工具,目的是逃过版主或管理员的匆匆一瞥。ROT13激励了广泛的线上书信撰写与字母游戏,且它常于新闻群组对话中被提及。

    2019-09-30
  • 沉淀的梦想
    文中说“对方主动关闭套接字,阻塞write调用会立即返回实际字节数,如果再次write,则返回失败”,这是的“关闭”只指两个方向关闭吗?如果对方只是半关闭的话,理论上本机还是可以继续write的吧

    作者回复: 对,是指close掉两个方向的。

    2019-09-29
  • 刘立伟
    ubuntu18.04环境下编译整个工程失败.提示如下
    CMakeFiles/aio01.dir/aio01.c.o: In function `main':
    aio01.c:(.text+0x19b): undefined reference to `aio_write'
    aio01.c:(.text+0x1f4): undefined reference to `aio_error'
    aio01.c:(.text+0x208): undefined reference to `aio_error'
    aio01.c:(.text+0x21d): undefined reference to `aio_return'
    aio01.c:(.text+0x329): undefined reference to `aio_read'
    aio01.c:(.text+0x379): undefined reference to `aio_error'
    aio01.c:(.text+0x38d): undefined reference to `aio_return'
    collect2: error: ld returned 1 exit status
    chap-30/CMakeFiles/aio01.dir/build.make:95: recipe for target 'bin/aio01' failed

    需要在在chapter-30 下的CMakeList.txt 中的target_link_libraries 增加 rt

    作者回复: 我是ubuntu 16.04 LTS版本,默认的pthread就可以支持了。

    2019-09-29
  • 石将从
    没有注释,看onSocketWrite和onSocketRead函数很费劲

    作者回复: 其实还是蛮简单的,稍微解释一下:

    onSocketRead是通过套接字读取数据,数据存放在Buffer对象里,Buffer对象通过了writeIndex记录当前数
    据区可写的位置;

    onSocketWrite通过套接字写数据,数据来源于Buffer缓冲对象,Buffer缓冲对象的readIndex记录了当前缓冲区读的位置。

    2019-09-28
  • 刘丹
    感觉 readable 这个标记的处理有点小问题,没太考虑多个 \n 的情况

    作者回复: 如果有多个\n,这里也认为客户端结束了,只不过\n会发送给客户端。

    可以加一个处理,如果读到\n,就不要再继续读下去了。

    2019-09-27
  • 程序水果宝
    应该把函数tcp_nonblocking_server_listen(SERV_PORT)的实现代码也给出来的,不然新手可能不知道怎么select 就变成了非阻塞了

    作者回复: 代码都在:https://github.com/froghui/yolanda

    这里贴一段:
        int listenfd;
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        fcntl(listenfd, F_SETFL, O_NONBLOCK);

    2019-09-27
    1
收起评论
19
返回
顶部