更新时间:2022-08-21 14:14:14
一般情况下,IO操作的行为受两种因素的影响:
同步就是指操作的发起和操作结果的获取由调用者完成.
异步指操作发起由调用方完成,操作结果由服务方主动告知.
IO操作一般可以分为4种:
阻塞IO对象在调用期间会阻塞IO函数,函数返回的时候,操作结果是明确的,因此不需要配合其他API来获取改操作的结果.
非阻塞IO对象在发生IO调用的时候,总是立即返回(返回的IO请求的结果,不是IO的执行结果),但执行结果不能马上得知,调用者可能需要使用配套的一系列API来获取IO结果(在什么时机,什么地点使用由调用方自己决定).
现在我们将IO对象放在套接字上,那么套接字有阻塞型套接字(Bolocking Socket)和非阻塞型套接字.再将目光放在windows操作系统上:
windows下套接字模型可以分为:
- Blocking Mode 阻塞型
- Non-blocking Mode 非阻塞型
windows下套接字IO模型:
- The blocking Model
- The select Model
- The WSAAsyncSelect Model
- The WSAEventSelect Model
- The Overlapped Model
- The Completion Port Model
备注:
- 上面六种基本上可以认为:按照从上到下的顺序,上面的使用最简单程度,依次递减,性能依次递增.
- 只要调用WSAEventSelect 或者WSAAsyncSelect, 套接字都会被自动设置为Non-Blocking.
- windows下典型的套接字API就是WSAXxxx家族函数,比如WSASend,WSARecv.因为WSAXxxx能够处理所有传统套接字API(如send,recv)的所有功能,我们只讨论前者.
Windwos下的WSAXxxx系列函数被设计成一套适合各种场景的API,看上去它非常的”聪明”,根据不同的场景有不同的语义:
WSAEWOULDBLOCK其实算不上一种严重的错误,在不同场景有不同的语义:
Function Name
Description
WSAAccept and accept
The application has not received a connection request. Call again to check for a connection.
closesocket
In most cases, this means that setsockopt was called with the SO_LINGER option and a nonzero timeout was set.
WSAConnect and connect
The connection is initiated. Call again to check for completion.
WSARecv, recv, WSARecvFrom, and recvfrom
No data has been received. Check again later.
WSASend, send, WSASendTo, and sendto
No buffer space available for outgoing data. Try again later.
1: int WSASend(
2: __in SOCKET s,
3: __in LPWSABUF lpBuffers,
4: __in DWORD dwBufferCount,
5: __out LPDWORD lpNumberOfBytesSent,
6: __in DWORD dwFlags,
7: __in LPWSAOVERLAPPED lpOverlapped,
8: __in LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
9: );
如果套接字句柄已经绑定到某个完成端口句柄上,回调函数必须设置为NULL,否则将得到10022(参数错误).如果IO结构体中的hEvent有效,内核仍然会为这个句柄设置信号.
重叠IO套接字上的操作通常都会立即返回,如果该操作可以立即完成则会返回字节数,否则返回(WSA_IO_PENDING),表示操作结果不能立即取得,从这个时候开始一直到完成事件到达,提交给内核的内存缓冲区将被锁定,调用方需要保证这期间该内存一直有效.因为在这种模式下,内核的策略是IO传输的数据直接缓冲到调用方的这块内存地址是,而不是套接字自身的缓冲区.这就为实现一个零拷贝(Zero Copy)的IO框架提供了可能性,试想一下,整个过程中不需要任何的memcpy,是不是很诱人?
此外,服务器也可能是一个CPU密集型服务,这个时候只需要改变一下套接字API(WSASend/WSARecv)的用法,很容易就从proactor模式切换为reactor模式,窍门就是使用零缓冲WSASend/WSARecv.
最后,一个常用的流程图:
图中的FIOBNIO=TRUE表示套接字设置是非阻塞,反之为阻塞套接字.