一些互联网公司似乎特别偏爱考网络的问题,因此需要花些时间来快速重温。
LINK: http://blog.163.com/tianle_han/blog/static/66178262008101555657759/
一、套接字(以后称为socket)模式和模型
1、socket也是计算机I/O的一种方式,socket的模式是指在调用有关socket的函数时,socket函数的行为方式。socket的两种模式分别为锁定和非锁定。锁定模式是指在有关socket的调用时使用socket的同步模式,同时同步模式也会阻塞当前线程或进程(linux),程序进入内核态运行,直到系统交还程序控制权,程序被重新唤醒。而非锁定方式是指socket的函数IO调用会立即返回,大多数情况下是调用“失败”,并立即返回WSAEWOULDBLOCK(windows)错误,所以通常我们需要重复调用一个函数直到成功,比如recv。windows下使用ioctlsocket来设置非锁定模式,使用这种模式时会耗费大量CPU,所以我们需要其他IO模型的帮助。
2、socket I/O模型是为了解决单独使用socket两种模式编程存在的限制,在两种模式基础上由操作系统提供的与操作系统特性紧密关联的应用程序对socket I/O进行管理和处理的方式。它和socket的两种模式无关,一些异步的I/O模型会自动更改socket的模式为非锁定的。
3、windows下的5种模型。
3.1 select I/O多路复用 模型
3.2 WSAAsyncSelect 窗口事件模型,此模型的核心点是把一个socket的通知事件绑定于某窗口的特定消息,这样当socket有指定的网络事件时,便会向此窗口发送此消息,从而引发窗口的消息处理函数winproc,我们可以在此窗口处理函数中编写代码查找socket的通知事件类型,然后做相应处理。大概的过程为:
3.3 WSAEventSelect 事件驱动模型
这种模型是将socket网络事件与windows事件绑定,当有网络事件发生时,我们的事件等待函数便返回,我们可以取得是哪个socket句柄的网络事件,然后再enum出具体的网络事件作相应处理即可。大概步骤如下:
3.4 重叠I/O
用于异步socket,在创建socket时需要在创建函数WSASocket中使用WSA_FLAG_OVERLAPPED标志,然后在投递IO请求的时候将一个Overlapped结构体指针赋给投递函数,可以使用WSAWaitForMultipleObject来监听事件,然后使用WSAGetOverlappedResult来获取IO的状态,也可以在Overlapped结构体中使用完成例程来处理,即在投递函数中把完成例程赋给投递函数。 大概的步骤如下:
当然也可使用完成例程,来投递I/O;可以在单独的线程中处理完成后的事件,而在另外的线程中处理I/O投递。
3.5 完成端口
它是迄今为止最复杂的一种IO模型,当应用程序需要管理众多的套接字并且希望随着系统内安装的CPU数目的增多,应用程序的性能也可以线性增加,就可以使用这种模型,它的原理是每个CPU可以单独负责一个线程的执行,避免线程的频繁切换。使用这种模型往往可以达到最佳的系统性能。首先需要使用CreateIOCompletePort来创建完成端口,然后将IO句柄和此端口绑定,绑定也是使用此函数,当然也可以一次完成。接着是创建工作者线程,工作者线程会使用GetQueuedCompletionStatus进入完成端口维护的线程池,当有完成事件时,会激活一个线程。 完成端口模型可以同时使用重叠I/O技术。
4、Linux下的5种模型
4.1 阻塞I/O模型(对应windows下的锁定模式)
4.2 非阻塞I/O(对应于windows下的非锁定模式)
4.3 I/O复用(对应于windows下的I/O复用模型)
此模型阻塞于select函数,当有网络事件发生时,再去执行相应操作。
4.4 信号驱动模型(对应于windows下的WSAAsyncSelect和WSAEventSelect模型)
我们像windows下使用WSAAsncSelect设定socket的窗口一样使用fcntl函数设定socket的拥有者,为socket开启信号驱动功能,然后为进程安装信号处理函数,这样在收到信号后便可以作相应处理。
4.5 异步I/O模型(对应于重叠I/O和完成端口模型)
此模型是对真正的异步操作模型,因为在进行I/O操作时不会引起进程的阻塞,而是在事件完成后由内核根据我们指定的方式来通知我们,通常的方式是我们创建多个工作者线程来处理完成信号,而其他的可以很多线程进行读写操作。而其他的模型在实质上均是同步I/O模型,因为它们在读写时总是阻塞线程,虽然它们有的使用了等待内核通知可读可写的信号(或事件)时才进行读写,但他们在读写时依然会阻塞线程,即从内核缓冲区拷数据或往内核缓冲区写数据时是阻塞的。