前言
本文主要介绍网络编程中的五种IO模型
本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接 C/C++后台高级服务器课程介绍 详细查看课程的服务。
提示:以下是本篇文章正文内容,下面案例可供参考
一、网络模块要处理哪些事情
在熟悉IO模型前首先需要明确网络模块(IO)的整个流程,也就是在一个完整的网络IO中网络模块要处理哪些事情,其实总的可以概括为五个步骤
建立连接: 创建socket,监听端口(bind&listen)
接收连接: 当连接到达时,调用accept接收客户端的连接请求,生成一个新的socket(后面以clientfd表示), 这里需要注意的是 clientfd 有两个作用,一个是可以用来检测 IO 是否就绪,一个是用来进行IO操作
连接断开: 监听两种断开状态(主动断开: 服务端读端关闭) (被动断开: 服务端读写端都关闭)
消息到达: 监听clientfd的读事件,当有消息时 从 内核空间(read buff) 把数据 拷贝到 用户空间(变量)
消息发送: 监听clientfd的写事件,把 用户空间 数据拷贝到 内核空间(write buff)
网络编程中主要关注 IO读写(内核空间拷贝到用户空间 or 用户空间拷贝到内核空间)的过程, 至于数据什么时候到达对端取决于网络协议栈(传输层, 网络层)
一、阻塞IO(blocking IO)
在linux中, socket默认是阻塞的,一个默认的IO阻塞操作步骤是这样的:
当用户进程调用了read方法, 如果这个时候网络数据还未到达,read buff中的数据还没准备就绪,这个时候用户进程将被阻塞等待,等待数据准备就绪后,用户进程再把数据从read buff 拷贝到 用户变量中返回,返回成功后用户进程才会被从阻塞等待数据的状态中解放出来,这边可以认为 等待数据就绪以及拷贝数据的过程 都是阻塞的
实际上send() recv() 等IO函数接口都是阻塞型的,在等待接口返回的过程中都是阻塞状态的。
这种模式就会导致一个IO阻塞 影响到 后续系统的网络连接请求以及进程IO无法被执行
二、非阻塞IO
linux下可以通过设置 socket 让他成为非阻塞IO 调用read流程如下
图中的流程是 当用户进程调用read 要读取数据时, 如果read buff中没有数据, 程序就直接返回-1,不会阻塞等待, 而用户进程知道此时还没有数据包,可以再次调用read 读取, 直到有数据进行内存拷贝返回给用户进程(注意: 此时read 具备了 IO检测的功能)
使用以下的函数接口可将IO设置成非阻塞状态
fcntl( fd, F_SETFL, O_NONBLOCK );