一、阻塞模式
1、通过socket()函数调用得到一个socket描述符时,默认为该socket采取阻塞模式。这意味着引用这个socket句柄的某些WInsock函数调用将会阻塞,直到操作完成。
阻塞式函数:
accept():应用程序收到一个连接请求。
closesocket():关闭操作完成(只是流socket阻塞,并且只在setsockopt()的SO_LINGER设置了非0超时值时)
connect():连接操作完成(只对流socket)
recv() recvfrom():网络系统在这个socket上接收到数据
send() sendto():网络系统中有输出数据可用的缓存空间(只对流socket)
2、还有一些函数总是阻塞的,无论传递给它的是否是一个阻塞的socket。
select():检测socket集合的可读性、可写性和错误
gethostbyname():通过主机名查询主机信息
gethostbyaddr():通过主机地址查询主机信息
getprotobyname():通过协议名查询协议号
getprotobynumber():通过协议号查询协议名
getservbyname():通过服务名查询服务信息
getservbyport():通过端口号查询服务信息
3、伪阻塞:
虽然应用程序在等待网络操作完成,但是WINSock DLL却在等待时进行了避让,在等待操作完成时,由于WinSock DLL调用了阻塞钩子函数,因此使阻塞变成了伪阻塞。
通常,默认的阻塞钩子函数调用WinAPI函数PeekMessage()、DispatchMessage()和TranslateMessage(),方式与Windows应用程序的消息循环相似。
重入消息:
应用程序在阻塞操作尚未完成时所接收的消息。当应用程序在前门等待阻塞操作完成时,他们将从后门溜出来。
4、阻塞情境:
只要存在一个未完成的阻塞函数,任何WInsock函数(无论它是否发起另一个阻塞操作)都可能失败并产生WSAEINPROGRESS错误信息。
如何避免阻塞情境所带来的问题:
1)如果已提交了一个WinSock调用,则正确应对所返回的WSAEINPROGRESS错误信息。
2)使用WSAIsBlocking()检测阻塞情境,避免WInSock调用。
BOOL PASCSL FAR WSAIsBlocking( void); //当前任务或线程即为输入参数
3)维持应用程序的状态信息,用以检测阻塞情境,避免WinSock调用。
可以维持一个代表应用程序当前状态的变量。例如,如果在状态变量中指明了connect()函数是成功的,你就会知道send()函数不会发生WSAENOTCONN错误。
5、撤销阻塞操作:
允许用户在应用程序的任何一点撤销阻塞操作是可能的,也可以使用一个Windows的定时器来实现自己的超时控制。函数WSACancelBlockingCall()允许通过强制其失败来强行结束一个等待的阻塞操作。
int PASCAL FAR WSACancelBlockingCall( void); //0 on success
任务或线程即为参数。失败时,函数返回SOCKET_ERROR错误,WSAGetLastError返回WSAEINVAL错误。
1)撤销作用滞后生效:
此函数总是立即返回,并不等待处理中的阻塞函数撤销完成,所以阻塞操作在此函数返回时仍然处于待决状态。当被中断的函数以WSAEINTR错误(被中断的系统调用)返回时,阻塞操作的撤销才完成。
2)限制socket的继续使用:
对某些阻塞socket调用的撤销可能会导致一个socket处于中间状态,或损害一个数据流的完整性。只有函数accept和select是例外。
3)撤销操作可能失败:
设置一个状态变量记录撤销请求,即使撤销操作失败(即阻塞函数成功返回),仍可以让应用程序继续执行撤销操作。当撤销生效时,任何阻塞函数必须能够处理返回的WSAEINTR错误。
6、自动超时:
诸如connect、send和gethostbyname的函数将会自动超时。其超时与所采用协议的超时机制的实现有关。
7、用户可设置的超时:
诸如select、closesocket的函数允许应用程序来决定超时值。对于closesocket函数,默认情况下它不是阻塞模式的,即使对于阻塞socket也是如此。因此,只有对一个阻塞socket,并且调用setsockopt()设置非0的超时值来使能SO_LINGER选项时,它才是阻塞式的。一般来说,应用程序不该为closesocket设置非0的超时值。
8、应用程序超时:
诸如recv、recvfrom或accept的函数能够永远阻塞下去。所以,当没有数据可接收或者没有接收到连接请求时,它们将一直不返回。
9、TCP存活超时:
为流socket提供超时控制功能的另一种方法是设置setsockopt()选项SO_KEEPALIVE。这将使协议族周期性地发送TCP”存活“数据包,接收方必须将数据包反馈给发送方作为对接收的确认。若没有收到反馈,则之后的任何I/O操作或者待处理的I/O操作都将失败并返回WSAETIMEOUT错误。
10、无最少接收限制值:
函数recv或recvfrom的输入参数len定义了输入数据缓存区的长度(即上限值)。
注意:长度域的值不是WinSock从阻塞函数recv或recvfrom返回前必须复制的最小字节数。这是一个常见的错误,会导致应用程序丢失数据。