一:select模型
二:WSAAsyncSelect模型
三:WSAEventSelect模型
四:Overlapped I/O 事件通知模型
五:Overlapped I/O 完成例程模型
六:IOCP模型
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型~~~
一:select模型
老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信 ~~~~~
在这种情况下,"下楼检查信箱" 然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。
select模型和老陈的这种情况非常相似:周而复始地去检查...... 如果有数据......接收/发送 .......
select模型是winsock中最广泛使用的模型之一,其核心就是select函数:
函数用于判断套接字上是否存在数据,或者是否可以向套接字里写入数据。他可以防止应用程序在套接字处于阻塞时send和recv函数也进入阻塞。防止产生大量的WSAEWOULDBLOCK错误。 它能够从单个线程的多个套接字上进行多重连接及IO。避免了伴随阻塞和多重连接的线程增加。
- int select(
- _In_ int nfds,//忽略
- _Inout_ fd_set *readfds,//检查可读性
- _Inout_ fd_set *writefds,//检查可写性
- _Inout_ fd_set *exceptfds,//检查是否有错误
- _In_ const struct timeval *timeout//函数等待的最大时间
- );
要注意的是,readfds,writefds,exceptfds三个参数必须至少有一个不为空。在任何不为空的集合中,必须包含至少一个套接字句柄。否则select函数就没有任何东西可以等待了。 再来说最后一个参数,他是指向timeval结构的指针。
当select调用成功,在fd_set结构中会存有满足一定条件的套接字组的子集。并且,select函数也会返回满足一定调节的套接字数目。如果超时那么就会失败,返回0或者SOCKET_ERROR。
- typedef struct timeval {
- long tv_sec;//秒
- long tv_usec;//毫秒
- } timeval;
二:WSAAsyncSelect模型
后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天......不是,微软~~~~~~~~
微软提供的WSAAsyncSelect模型就是这个意思。
WSAAsyncSelect模型是Windows 下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。
这个模型是以消息为基础的,关键就是WSAAsyncSelect函数了:
- int WSAAsyncSelect(
- _In_ SOCKET s,//指定套接字
- _In_ HWND hWnd,//指定窗口句柄
- _In_ unsigned int wMsg,//指定发生网络事件时,打算接受的消息
- _In_ long lEvent//指定一个位掩码
- );
将socket消息发送到hWnd窗口上面;然后在那里处理相应的消息。
他喝WSAEventSelect模型都提供读写数据能力的异步通知,不提供异步数据传送。(Overlapped IO提供) 他们在系统开销不大的时候可以同时处理多个连接。 相比之下select模型还需要建立fd_set结构。 但是这里你必须要使用一个窗口来接收消息,如果套接字多了该怎么办;
三:WSAEventSelect模型
后来,微软的信箱非常畅销,购买微软信箱的人以百万计数......以至于盖茨每天 24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出"新信件到达"声,提醒老陈去收信。盖茨终于可以睡觉了。
此模型也是以时间为基础的网络事件通知。它主要由事件对象句柄来完成的,不是通过窗口,通过函数原型就能看出来。 但是他每次只能等待64个事件,处理多个套接字时有必要组织一个线程池来进行。他的延伸性不如完成端口。
- int WSAEventSelect(
- _In_ SOCKET s,//一个套接字描述符
- _In_ WSAEVENT hEventObject,//指定所提供的FD_XXX网络事件集合相关的一个事件对象句柄
- _In_ long lNetworkEvents//一个屏蔽位,指定感兴趣的FD_XXX网络事件性组合
- );
四:Overlapped I/O 事件通知模型
后来,微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!老陈很高兴,因为他不必再亲自收发信件了!
Overlapped I/O 事件通知模型和WSAEventSelect模型在实现上非常相似,主要区别在"Overlapped",Overlapped 模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。这些提交的请求完成后,应用程序会收到通知。什么意思呢?就是说,如果你想从 socket上接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区 ~~~~~
五:Overlapped I/O 完成例程模型
老陈接收到新的信件后,一般的程序是:打开信封----掏出信纸 ----阅读信件----回复信件......为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信 /阅读/回复了!老陈终于过上了小资生活!
上面两个模型我们称为重叠模型,这个模型可以使程序达到更佳的性能。设计原理是:让应用程序使用重叠的数据结构,一次投递一个或多个IO请求,针对这些提交的请求,在他们完成之后,应用程序可以为他们提供服务。在上面两种模型分别是:在事件中使用,在完成例程中使用。
六:IOCP模型
微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏 ......
微软给每个大公司派了一名名叫"Completion Port"的超级机器人,让这个机器人去处理那些信件!
"Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的 [没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文 [Context],线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。我们不妨设想一下:如果事先开好 N个线程,让它们在那hold[堵塞 ],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N 个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题, Microsoft又怎会没有考虑到呢?"----- 摘自nonocast的《理解I/O Completion Port》
ok,最后是我们的完成端口,他提供了很好的伸缩性,往往可以使系统达到最好的性能,是处理成千上万的套接字的首选; 完成端口要求创建一个windows完成端口对象,这个对象通过制定数量的线程,对重叠IO请求进行管理,以便为已经完成的重叠IO提供服务;