Blocking/Non-Blocking VS Sync/Async VS Overlapped

[转载请注明:出自cnblogs,作者:byeyear,Email:[email protected]]

[2012.10.08: 完善文章内容]

本文主要讨论几个在网络编程中经常被讨论但也经常被误解的概念:

阻塞(Blocking),非阻塞(Non-Blocking),同步(Sync),异步(Async),重叠(Overlapped)。

首先要明确的是,只有那些会导致发生“数据传输”的函数(accept,connect,send,recv等)才存在这些概念;像bind、listen这些不会引发“数据传输”的函数,是不存在“阻塞非阻塞”或“同步异步”这样的问题的。

其次一个比较重要的问题是,Non-Blocking和Async不是一回事。在一些论坛文章和关于网络编程的书中,将这两者等同,这是不正确的。

1. Blocking和Non-Blocking

Blocking:只有在函数所请求的操作完成后,函数才会返回。例如,accept会等到连接队列非空才返回;recv会等到数据缓冲区中有数据后才返回。因此,blocking的意思是,操作若不能完成函数就不返回。

Non-Blocking:如果操作无法立即完成,send/recv这样的函数将以失败返回,函数所请求的操作不会得到执行。同时WSAGetLastError函数将返回WSAEWOULDBLOCK。例如,如果缓冲区中没有数据,recv会立即返回。在Non-Blocing模式下,程序可以通过一个循环不停调用recv,直到有数据可用。

2. Sync、Async和Overlapped

Sync:App层的函数调用和OS底层的动作是顺序的。例如,App调用recv -> OS执行recv -> 执行完成 -> 函数返回。

Async:App层的函数调用和OS底层的动作是非顺序的。例如,App以填充好的Overlapped结构调用WSARecv,即使无数据可读,函数也会立即返回,但OS底层仍然会继续执接收数据的工作,并在有数据可用时以event、callback或completion port的形式通知App。而Overlapped和Async实际上是一个概念。之所以叫“Overlapped”,是指多次函数调用可以重叠进行。例如,你可以在有数据可读前多次调用WSARecv。如果一个Async IO不能立即完成,函数将以失败返回,同时你WSAGetLastError将返回WSA_IO_PENDING。但是,底层的操作将继续执行。你的程序将在稍后得到通知。

3. Blocking/Non-Blocking通过ioctlsocket设定,设定完成后,后续的send/recv将遵循最经一次的ioctlsocket设置。

4. overlapped和两方面有关:一是通过WSASocket创建socket时是否设定了overlapped标记(socket默认设置overlapped),二是WSASend/WSARecv调用是否提供overlapped结构。

    4.1 如果WSASocket没有设定overlapped,那么这个socket将是sync的且不可改变,WSASend/WSARecv的行为和send/recv类似;

    4.2 如果WSASocket设定了overlapped,那么有三种情况:

        4.2.1 使用send/recv。这两个函数不支持overlapped,所以函数行为是sync的;

        4.2.2 使用WSASend/WSARecv,但不提供overlapped结构。函数行为同样是sync的;

        4.2.3 使用WSASend/WSARecv,并且提供overlapped结构。函数行为时Async的。App将在数据可用时得到通知。

    从上文可以看出,即使为socket设定了overlapped attribute,仍然可以对该socket使用非overlapped函数调用。换句话说,ASync/overlapped仅是socket所具备的一个“功能”,你可以用,也可以不用。这和Non-Blocking是不同的,在那里,一旦设定了Non-Blocking,函数调用必定是Non-Blocking的。

综上,我们可以得到socket I/O操作的几种方法:

a) Blocking,Sync:这个是最简单的。连接建立后,App调用recv或send收发数据。

b) Non-Blocking,Sync:使用ioctlsocket将指定socket设置为Non-Blocking。在ioctlsocket之后,若数据不可用,recv或send将返回WSAEWOULDBLOCK。

c) Blocking,Async:若无数据可用,send或recv函数将Blocking。WSARecv和WSASend将立即返回,并在有数据可用时通知App。

d) Non-Blocing,Async:在无数据可用的情况下,send/recv将返回WSAEWOULDBLOCK,WSARecv/WSASend将返回SOCKET_ERROR。区别在于,send/recv除了立即返回以外不会有别的效果,而WSARecv和WSASend虽然也是立即返回,但它们所请求的操作将由OS继续执行,直到有数据可用后通知App。

宝书《Windows网络编程》中所述的几种Model,实际上就是Blocking/Non-Blocking和Sync/Async的不同组合。

a) 阻塞模式:Blocking + Sync

b) 非阻塞模式:Non-Blocking + Sync。App通过不停调用send/recv获取数据。

c) select模式:Blocking + Non-Sync。和a)的区别在于,阻塞在select上而不是send/recv上。select保证后续的send/recv操作不会阻塞,看起来好像是Non-Blocking一样——但实质上仍然是Blocking模式。

d) WSAEventSelect/WSAAsyncSelect:Non-Blocking + Sync。这两个函数会自动将socket设置为Non-Blocking(MSDN如是说)。和b)的区别在于由OS负责在数据可用时发出Message或SetEvent,不需要App自己Polling。

e) Overlapped:Blocking + ASync。在这种情况下,一般不会使用send/recv函数而是使用WSASend或WSARecv,所以Blocking不会成为问题。如果你不小心在这种model下使用了send/recv,那么这两个函数仍然会在无数据可用的情况下blocking住。

 参考文档:http://support.microsoft.com/kb/181611/en-us?fr=1

你可能感兴趣的:(async)