阻塞、select、WSAAsyncSelect、WSAEventSelect、Overlapped I/O 事件通知 、Overlapped I/O 完成例程、IOCP

老陈通过信件联系在外地工作不能经常回来的女儿

信会被邮递员投递到他们的信箱里

这和Socket模型非常类似


零、阻塞模型

老陈非常想看女儿的信,什么都不做,就站在门口等到接到邮递员给他的信件才开心的看信回信

进程阻塞在socket的接收函数上

一、select模型

不吃不喝一直站门口等不行,每隔10分钟下楼检查信箱是否有信

在这种情况下,“下楼检查信箱” 然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作

周而复始地去检查…… 如果有数据……接收/发送 …….

服务端主要动作:

  1. 创建监听socket,绑定,监听
  2. 创建worker线程
  3. 创建一个socket数组,存放所有活动客户端socket,每accept一个连接就更新一次数组
  4. 接受客户端的连接
  5. 所有请求的交互,都要遍历一次数组,取出匹配的连接再操作

二、WSAAsyncSelect 异步选择模型

老陈使用了微软公司的新式信箱,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了

Windows 下最简单易用的一种Socket I/O模型。Windows把网络事件以消息的形式通知应用程序

服务端主要动作:

  1. WM_CREATE消息处理函数中,初始化Windows Socket library,创建监听socket,绑定,监听,并且调用WSAAsyncSelect函数表示我们关心在监听socket上发生的FD_ACCEPT事件

  2. 自定义一个消息WM_SOCKET,一旦在我们所关心的套接字(监听套接字和客户端套接字)上发生了某个事件,系统就会调用WndProc并且message参数被设置为WM_SOCKET

  3. 在WM_SOCKET的消息处理函数中,分别对FD_ACCEPT、FD_READ和FD_CLOSE事件进行处理

  4. 在窗口销毁消息(WM_DESTROY)的处理函数中,我们关闭监听套接字,清除Windows Socket library

特点:需要建立一个窗口用于接收消息

三、WSAEventSelect 模型

微软的信箱非常畅销,购买微软信箱的人以百万计数……以至于盖茨每天 24小时给客户打电话,累得腰酸背痛,喝红牛都不好使~~~~~~

微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出 “新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了

服务端主要动作

  1. 感兴趣的一组网络事件创建一个事件对象,再调用WSAEventSelect函数将网络事件和事件对象关联起来

  2. 网络事件发生时,winsock使响应的事件对象受信,在事件对象上等待的函数就会立即返回

  3. 调用WSAEnumNetworkEvents函数便可获得到底发生了什么网络事件(FD_READ/FD_ACCEPT/FD_CLOSE等等)

特点:最多可以支持WSA_MAXIMUM_WAIT_EVENTS(64)个对象

四、Overlapped I/O 事件通知模型

微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间

于是微软再次改进他们的信箱,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!

老陈很高兴,因为他不必再亲自收发信件了!

Overlapped I/O 和WSAEventSelect实现上非常相似,Overlapped 模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个 Winsock I/O请求

这些提交的请求完成后,应用程序会收到通知。什么意思呢?

如果你想从 socket上接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区

五、Overlapped I/O 完成例程模型

老陈接收到新的信件后,一般的程序是:打开信封—-掏出信纸 —-阅读信件—-回复信件 ……

为了进一步减轻用户负担,微软又开发了一种新的技术: 用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信 /阅读/回复了!老陈终于过上了小资生活!

Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数

特点:由I/O来完成socket的拆包工作,实现异步

六、IOCP 完成端口模型

微软信箱似乎很完美,老陈也很满意

但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏 ……

微软给每个大公司派了一名名叫”Completion Port”的超级机器人,让这个机器人去处理那些信件!

特点:动用一个合理数量的线程来接受信息,然后把信息投送到应用程序


“Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中

因为所有这些线程都是可运行的 [没有被挂起和等待发生什么事], Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作

大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没 有开销的

我们不妨设想一下:如果事先开好 N个线程,让它们在那hold[堵塞 ],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N 个 线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程

不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题, Microsoft又怎会没有考虑到呢?”—– 摘自nonocast的《理解I/O Completion Port》