很幽默的讲解六种Socket I/O模型
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型。
老陈非常想看到女儿的信,以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信。在这种情况下,“下楼检查信箱”然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。select模型和老陈的这种情况非常相似:周而复始的去检查,如果有数据,接收/发送。
使用线程来select应该是通用的做法:
后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新信件了。从此,老陈再也不必频繁的下楼检查信箱了。
微软提供的WSAAsyncSelect模型就是这个意思。
WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形式通知应用程序。
首先定义一个消息标识常量:
#define WM_SOCKET WM_USER + 1
再在住form中添加一个处理该消息的函数声明:
afx_msg DWORD WINAPI OnSocket(WPARAM wParam, LPARAM lParam);
然后再做消息映射:ON_MESSAGE(WM_SOCKET, OnSocket)
然后就可以使用WSAAsyncSelect模型了,
WSAAsyncSelect(m_socket, hThread, WM_SOCKET, FD_ACCEPT | FD_CLOSE);
应用程序可以对收到WM_SOCKET消息进行分析是哪一个socket产生了网络事件以及事件类型。
后来,微软的信箱非常畅销,购买微软信箱的人以百万计数,以至于盖茨每天24小时给客户打电话,累的腰酸背疼,喝蚁力神都不好使。。。
微软改进了他们的信箱:在客户的家中添加了一个装置,这个装置会监视客户的信箱,每当新的信件到来时,此装置会发出“新信件到达”的提示,告诉老陈去收取信件。
同样要使用线程:
后来,微软通过调查发现,老陈不喜欢上下楼收取信件,因为上下楼其实非常费时间。于是微软再次改进他们的信箱。新式信箱才用了更为先进的技术,只要用户告诉微软自己家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了。老陈很高兴,因为他不必再亲自收取信件了。
Overlapped I/O 事件通知模型和WSAAsyncSelect 模型在实现上非常相似,主要区别在"Overlapped",Overlapped 模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。这些提交完成之后,应用程序会收到通知。什么意思呢?就是说,如果你想从socket接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区。。。
Listen线程和WSAEventSelect模型一模一样,Recv/Send线程则完全不同:
老陈接收到信件后,一般的程序是:打开信封--掏出信纸--阅读信件--回复信件。。。为了进一步减轻用户负担,微软又开发了一种新技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自去拆信/阅读/回复了。
Overlapped I/O 完成例程模型要求用户提供一个回调函数,发生网路事件的时候系统将执行这个函数:
微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同。这个大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃。需要重新启动。微软不得不使出杀手锏。。。
微软给每个大公司派了一名名叫“Completion Port”的超级机器人,让这个机器人去处理那些信件。
Window NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的【没有挂起和等待发生什么事】,微软意识到NT内核花费太多的时间来运转线程上下文【Context】,线程就没有得到很多CPU时间来做他们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比创建进程开销要小,但也远不是没有开销的。我们不妨设想一下:如果事先开好N个线程,让它们在那【阻塞】,然后可以将所用用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出的问题,微软又怎么会没有考虑到呢?摘自nonocast的《理解I/O Completion Port》
先看一个IOCP模型的实现: