cppBoost06~09网络IO编程(一)

五种网络IO模型(重要)

1、阻塞式IO

2、非阻塞式IO

<span style="background-color:#f8f8f8"><span style="color:#333333"> <span style="color:#000000">fcntl</span>; <span style="color:#000000">O_NONBLOCK</span>;
 ​
 <span style="color:#008855">int</span> <span style="color:#000000">flags</span> <span style="color:#981a1a">=</span> <span style="color:#000000">fcntl</span>(<span style="color:#000000">sockfd</span>, <span style="color:#000000">F_GETFL</span>, <span style="color:#116644">0</span>);
 <span style="color:#000000">fcntl</span>(<span style="color:#000000">sockfd</span>, <span style="color:#000000">F_SETFL</span>, <span style="color:#000000">flags</span><span style="color:#981a1a">|</span><span style="color:#000000">O_NONBLOCK</span>);</span></span>
3、IO多路复用

4、信号驱动IO

5、异步IO

总结

Notes:

  • 同步:在同步操作中,调用者发出一个请求或者指令后,必须等待操作完成并且得到结果后,才能继续进行后续操作。这意味着操作的执行是按照顺序执行的。

  • 异步:在异步操作中,调用者发出请求后,不需要等待操作完成,可以立即继续执行其他操作。当操作完成后,系统会通过回调函数、通知机制或事件来处理结果。

  • 阻塞:在程序中,当某一个线程或进程执行某个操作时,如果该操作无法立即完成,那么这个进程或线程就会被挂起,直到操作完成。

  • 非阻塞:非阻塞操作允许线程或进程在执行某个操作时,如果操作不能立即完成,就会返回一个状态,但不会挂起哦这个线程或者进程

六、并发服务器模型

1、循环式迭代式模型

一种单线程的应用程序,它只能使用短连接而不能使用长连接,缺点是无法充分利用多核CPU,不适合执行时间较长的服务,即适用于短连接(这样可以处理多个客户端),如果是长连接则需要在read/write之间循环,那么只能服务一个客户端。所以循环式服务器只能使用短连接,而不能使用长链接,否则无法处理多个客户端的请求,因为整个程序是一个单线程的应用程序。

2、并发式服务器

适合执行时间比较长的服务。在父进程中要关闭创建连接的套接字,等待下一个客户端请求连接,所以可以并发式服务器处理多个客户端的请求,一个客户端一个进程;子进程在处理客户端的请求,子进程是长连接的,不断的处理请求,即使子进程中解包,计算,打包的过程时间过长,也不会影响父进程去连接其他客户端的请求。本模型也适用于线程,主线程每次accept 回来就创建一个子线程服务,由于线程共享文件描述符,故不用关闭,最后一个线程关闭监听套接字。

3、prefork服务器

它处理连接的进程/线程都是预先创建好的,因此可以减小创建进程/线程的开销,能够提高响应速度。进程预先fork了n个子进程(n的数目需要提前设定),每个子进程负责和客户端的通信,每个子进程执行的都是右图的流程。前面的创建套接字,绑定端口号,监听套接字这些步骤,每个预先创建的进程已经完成,他们分别调用accept并由内核置入睡眠状态。这种服务器的优点是:提高了响应速度,不需要引入父进程执行fork的开销,新客户就能得到处理。缺点在于:每次启动服务器,父进程必须预测到底需要产生多少子进程,还有一个就是如果不考虑再派生子进程,先前派生的子进程可能被客户请求占用完,以后新到的请求只能先完成三次握手,并且达到listen接口的最大并发连接数backlog,直到有子进程可用。服务器才调用accept,将这些已经完成的连接传递给accept。

4、反应式(Reactor)服务器

服务器可以并发处理多个请求。不过本质上这些请求还是在一个线程中完成的,即:单线程轮询多个客户端。也无法充分利用多核CPU,不适合执行时间比较长的服务,所以为了让客户感觉是在“并发”处理而不是“循环”处理,每个请求必须在相对较短时间内执行。当然如果这个请求不能在有限的时间内完成我们可以将这个请求拆分开来,使用有限状态机机制来完成。其中的Reactor可以使用IO多路复用select/poll/epoll。

模型的过程解析:

  • Reactor是一个线程对象,该线程会启动事件循环,并使用select/poll/epoll来实现IO多路复用。注册一个Acceptor事件处理器到Reactor中,Acceptor事件处理器所关注的事件是accept事件,这样Reactor会监听客户端向服务器端发起的连接请求事件。

  • 客户端向服务器端发起一个连接请求,Reactor监听到了该accept事件的发生并将该accept事件派发给相应的Acceptor处理器来进行处理。Acceptor处理器通过accept方法得到与这个客户端对应的连接,然后将该连接所关注的读事件以及对应的read读事件处理器注册到Reactor中,这样Reactor就会监听该连接的read事件了。或者当你需要向客户端发送数据时,就向Reactor注册该连接的写事件和其处理器。

  • 当Reactor监听到有读或者写事件发生时,将调用dispatch将相关的事件分发给对应的处理器进行处理。比如,读处理器会通过read方法读取数据,此时read操作可以直接读取到数据,而不会堵塞与等待可读的数据到来。

  • 每当处理完所有就绪的I/O事件后,Reactor线程会再次执行IO多路复用的函数阻塞等待新的事件就绪并将其分派给对应处理器进行处理。

目前的单线程Reactor模式中,不仅I/O操作在该Reactor线程上,连非I/O的业务操作也在该线程上进行处理了,这可能会大大延迟I/O请求的响应。所以我们应该将非I/O的业务逻辑操作从Reactor线程上分离出去,以此来加速Reactor线程对I/O请求的响应。这种的服务器的并发量比并发式服务器多,因为并发式服务器能够创建的进程或者线程数目是有限的。

5、反应式 + 线程池型服务器

与单线程Reactor模式不同的是,增加了线程池对象,并将业务逻辑的处理从Reactor线程中移出转交给 工作的线程池来执行。这样能够提高Reactor线程的I/O响应,不至于因为一些耗时的业务逻辑而延迟对 后面I/O请求的处理。

6、多反应式服务器

一个主线程,多个工作线程,主线程Reactor负责接收客户端连接,每个线程有各自的Reactor负责执行 任务队列中的任务。

7、多反应式+ 线程池模型

多个Reactor的模式,mainReactor与subReactor都是一个线程,因为多进程之间无法共享计算线程池。这种模型能够适用IO频繁且计算密集的服务。

总结:

对高并发编程网络连接上的消息处理,可以分为两个阶段:等待消息准备好、消息处理。当使用默认的阻塞套接字时(例如上面提到的 1 个线程捆绑处理 1 个连接),往往是把这两个阶段合而为一,这样操作套接字的代码所在的线程就得睡眠来等待消息准备好,这导致了高并发下线程会频繁的睡眠、唤醒,从而影响了 CPU 的使用效率。

高并发编程方法当然就是把两个阶段分开处理。即,等待消息准备好的代码段,与处理消息的代码段是分离的。当然,这也要求套接字必须是非阻塞的,否则,处理消息的代码段很容易导致条件不满足时,所在线程又进入了睡眠等待阶段。那 么问题来了,等待消息准备好这个阶段怎么实现?它毕竟还是等待,这意味着线程还是要睡眠的!

解决办法就是,线程主动查询,或者让 1 个线程为所有连接而等待!这就是 IO 多路复用了。多路复用就是处理等待消息准备好这件事的,但它可以同时处理多个连接!它也可能 ”等待 ”,所以它也会导致线程睡眠,然而这不要紧,因为它一对多、它可以监控所有连接。这样,当我们的线程被唤醒执行时,就一定是有一些连接准备好被我们的代码执行了。作为一个高性能服务器程序通常需要考虑处理三类事件: I/O 事件,定时事件及信号。两种 高效 的事件处理模型: Reactor 和 Proactor 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值