网络模型综述

UNIX网络I/O模型

1、阻塞I/O

Socket设置为阻塞模式,当socket不能立即完成I/O操作时,进程或线程进入等待状态,直到操作完成。如下图:

 

这种模型非常经典,也被广泛使用,优势在于非常简单,等待的过程中占用的系统资源微乎其微,程序调用返回时,必定可以拿到数据;

但简单也带来一些缺点,程序在数据到来并准备好以前,不能进行其他操作,需要有一个线程专门用于等待,这种代价对于需要处理大量连接的服务器而言,是很难接受的

 

2、非阻塞I/O

把socket设置成非阻塞模式,与阻塞模式不同的是:无数据时,也不会进入等待,而是立即返回特定错误,如下图:


这种模式在没有数据可以接收时,可以进行其他的一些操作,比如有多个socket时,可以去查看其他socket有没有可以接收的数据; 实际应用中,这种I/O模型的直接使用并不常见,因为它需要不停的查询,而这些查询大部分会是无必要的调用,白白浪费了系统资源;非阻塞I/O应该算是一个铺垫,为I/O复用和信号驱动奠定了非阻塞使用的基础。

 

3、I/O复用

I/O复用模型能让一个或多个socket可读或可写准备好时,应用能被通知到;I/O复用模型早期用select实现,它的工作流程如下图: 

 

这种模型的使用场景一般有这样一个共同特点:都有多个socket需要处理,这样能在获取I/O事件时复用同一个等待机制。比如监听服务器,既要处理监听的socket,又要处理连接的socket。 I/O复用是应用场景较多的一种模式,socket连接数多时,大多会采用它。除了select以外,I/O复用的还可以用poll、epoll、kqueue(freebsd)来实现,后两者在处理大量连接时性能上有很大的提高。

 

4、信号驱动

信号驱动模型是在socket准备好的时候用信号的方式进行通知,然后应用程序从内核读取数据。 然而,对于socket,SIGIO触发意味着多种可能,对于UDP有两种,对于TCP,则有7种,要想区分是何种操作引起的signal都是一件困难的事情,所以这种模型很少被实用,直到内核2.3起,引入了POSIX RT-Signal机制以后,这一现象得到些许改善。

 

5、异步I/O

在标准Unix下,异步I/O是由“aio_XXX”接口 提供的,它把一个信号和值与每一个I/O操作关联起来。异步I/O是POSIX 1003.1b实时标准的扩展,也属于Single Unix Specification,version 2。 几年前,Ben LaHaise实现了Linux AIO,合并到了2.5.32的内核中,在2.6时它正式成为标准特性。然而,令人遗憾的是,它目前还不支持对socket的操作,相信不久以后会完善起来。 异步I/O的模型与I/O复用和信号驱动颇有些相似,但最大的区别是:信号到达时,I/O操作已经由内核完成,应用只需要继续处理数据就好,POSIX的AIO的操作流程如下:

 

Windows网络I/O模型

Windows中I/O模型与UNIX有一些类似的地方,差异也少: 阻塞I/O、非阻塞I/O、 I/O复用使用起来基本一样; windows的signal不支持SIGIO,这样也就没有了基于signal的信号驱动模型,不过windows利用自身的窗口句柄和Event也做出了类似的东西,它们分别是WSAAyncSelect和WSAEventSelect; 对于异步I/O,windows已经有了自己的解决方案,并且支持对socket的操作,那就是IOCP

 

1、WSAAyncSelect与WSAEventSelect

WSAAyncSelect用窗口句柄和自定义的消息,来传递socket事件,典型的windows处理逻辑。当READ、WRITE、ACCEPT等事件发生时,与Socket句柄绑定的窗口会收到指定的消息,消息的参数可以判断发生了什么样的网络事件; WSAEventSelect与WSAAyncSelect不同,它不用窗口而用Event来传递socket事件,同时用WSAWaitForMultipleEventsEvent等待到Event事件,再通过WSAEnumNetworkEvents获取到底有哪些网络事件发生了;需要注意的是:由于系统的限制,WSAWaitXXX函数里wait的Event最多只能有64个。 两者的相同之处在于,收到网络事件以后,它们还需要主动去操作数据; 性能上,使用Event来通知肯定更为迅速。

 

2、Overlapped I/O

Windows下的异步IO,IOCP的基础,就像非阻塞I/O是I/O复用的基础一样;重叠I/O的数据结构体如下,注意其中有一个Event对象

在创建socket时,声明该socket为重叠模式,此时需创建一个WSAOVERLAPPED对象,当调用发送或者接收时,把这个对象传入;然后,可以等待WSAOVERLAPPED对象中的Event来获知网络事件,这个过程与WSAEventSelect有些类似,与之不同的是要使用WSAGetOverlappedResult来获取重叠IO返回的结果(注意,这里获取的是结果,也就是说当Event触发时,它已经把数据准备好了,是异步IO的处理方法); 虽然Overlapped I/O已经是异步IO,但它在处理大量并发请求时比WSAEventSelect好不了多少,这样就有了IOCP。

 

3、IOCP

采用Overlapped I/O的一个异步I/O框架,它的优势是操作大量句柄时效率更高。IOCP允许多个socket绑定到一个完成端口(可理解为比较特别的句柄)上,同时,在线程池的工作线程中监听完成端口,以此来获得这些文件句柄的IO操作结果。注意,这里不是获取IO操作的事件,而是结果,典型的异步I/O模式。

IOCP有这样一些特性:

1.一个完成端口可绑定多个文件句柄

2.多个线程同时访问一个完成端口,没有线程安全问题

3.一个线程最多只能关联一个完成端口,GetQueuedCompletionStatus调用时进行绑定,线程运行时可以换绑,绑定结束条件:换绑、线程退出、完成端口被释放

4.完成端口创建时,有最大关联线程数,超过这个最大值的线程,会被block,直至关联线程数减少到最大值以下

5.完成端口关联线程数量的取值可参考CPU核数,如果线程中处理数据的流程比较长,则应加大这个值

6.异步IO完成前,线程等待的是完成端口,而不是某个文件句柄的异步IO操作

7.一个异步IO完成时,系统会把完成数据(Complete Packet)放入一个FIFO队列

8.一个异步IO完成时,最后一个wait的线程会被释放(LIFO),这样可以减少线程切换

9.可以通过PostQueuedCompletionStatus发送完成数据包,这样可以做一些自己的扩展

10.IOCP在线程逻辑简单,同时异步IO并发量较大时,效率更高

 

多路复用模型(Multiplexing)

多路复用技术可以用来提高网络I/O的使用效率,是所有大中型网络框架都必须使用的技术手段,I/O复用实现、异步I/O实现都可以实现多路复用模型; ACE提出了两种经典的多路复用模型:Proactor和Reactor

 

1、Reactor

Reactor一般用I/O复用实现,其工作流程如下图:


1.注册读就绪事件和相应的事件处理器;
2.事件分离器等待事件;
3.事件到来,激活分离器,分离器调用事件对应的处理器;
4.事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

 

2、Proactor

Proactor一般用异步I/O实现;与reactor不同的是,处理器不关心I/O就绪事件,它关注的是完成事件;
下图是Proactor的流程:


1.处理器发起异步操作,并关注I/O完成事件;
2.事件分离器等待操作完成事件;
3.分离器等待过程中,内核并行执行实际的I/O操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成;
4.I/O完成后,通过事件分离器呼唤处理器; 5.事件处理器处理用户自定义缓冲区中的数据;

 

线程(进程)模型

1、单线程模型

单个线程采用多路复用技术管理所有socket,socket全部设置non-blocking模式,由事件通知触发网络读写;在处理大量连接时,是非常经典的线程模型之一;
这种模型工作方式如下图所示:

 

需要注意的是,由于所有的socket操作都在一个线程中完成,所以必须保证在线程中,除了网络I/O操作以外,没有其他引发阻塞的调用。

 

2、多线程模型

2.1 每个连接对应一个线程

一个网络socket对应一个处理线程,socket采用阻塞I/O模型;这种模型是小程序和java常用的策略,对于交互式的长连接应用也是常见的选择(比如BBS),也常用来做内部服务器交互的模型。 这种策略很难满足高性能程序的需求,好处是实现极其简单,容易嵌入复杂的交互逻辑。Apache、ftpd等都是这种工作模式。

 

2.2 线程池

线程池一般有两种模式:Half-Sync/Half-Async模式和Leader/Followers模式

 

2.2.1 半同步、半异步模式(Half-Sync/Half-Async)

这种模式有三部分组成:异步事件接收层、事件同步队列、同步事件处理层; 其中,异步事件接收层为一个线程,同步事件处理层可以有多个线程; 它的工作流程很清晰:

1.异步线程负责检查网络的异步事件

2.发生网络事件时,异步线程把网络事件放入事件队列

3.同步线程从队列中获取网络事件,并执行同步的读或写操作




这个过程需要注意的是,不要引起两个同步线程同时接收或发送一个socket的情况。

 

2.2.2 领导者、追随者模式(Leader/Followers)

这种模式与Half-Sync/Half-Async完全不同,没有事件队列,没有固定的事件接收者,每个线程都是事件接收者,也是处理者

Leader/Followers的流程:

1.准备若干个线程用来处理大量的事件

2.有一个线程作为Leader,等待事件的发生。其他的线程作为Follower,仅仅是睡眠

3.有事件需要处理时,如果Leader能很快处理掉,Leader会再次进入等待状态

4.如果Leader不能马上处理完,Leader则从Followers中指定一个新的Leader,自己去处理事件,不再当Leader

5.被唤醒的Follower作为新的Leader等待事件的发生

6. 处理事件的线程处理完毕以后,就会成为Follower的一员,直到被唤醒成为Leader。IOCP就是典型的L/F的工作模式,当线程1从GetQueuedCompletionStatus这里返回后,如果线程1的处理过程没有超过某个时间段,而是很快就返回,之后继续GetQueuedCompletionStatus,那OS会让新到的数据从线程1的GetQueuedCompletionStatus获取,这样就减少了线程的CONTEXT切换代码。反之,如果线程1处理时间比较长,那么新到的数据将会由线程2的GetQueuedCompletionStatus获得


 

多进程模型

一个客户端对应一个进程来处理,也是一种历史悠久的网络模型,linux的典型例子就是inetd服务。这种方式用来处理间断性内部数据处理时,比其常驻内存的stand-alone模式更节省系统资源。

 

成熟的IO框架介绍

ACE

“重量级的C++ I/O框架,用面向对象实现了一些I/O策略和其它有用的东西,特别是它的Reactor是用OO方式处理非阻塞I/O,而Proactor是用OO方式处理异步I/O的( In particular, his Reactor is an OO way of doing nonblocking I/O, and Proactor is an OO way of doing asynchronous I/O). ” 从很多实际使用来看,ACE是一个很值得学习的网络框架,但由于它过于重量级,导致使用起来并不方便。 ACE中提出了两种网络模式:Proactor和Reactor。

 

ASIO

“C++的I/O框架,逐渐成为Boost库的一部分。it’s like ACE updated for the STL era。” 支持select、epoll、IOCP等IO模型

 

libevent

由Niels Provos用C编写的一个轻量级的I/O框架。它支持kqueue和select、poll和epoll。 1.4.11版还不支持windows的IOCP,但已经有很多开发者自己修改源码,把IOCP合并进去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值