Windows 套接字 1.1 阻止例程和 EINPROGRESS
将应用程序从 Berkeley Sockets 环境移植到 Windows 环境的一个主要问题涉及阻止;也就是说,调用在关联操作完成之前不返回的函数。 当操作需要任意很长的时间才能完成时,会出现问题:例如 ,recv 函数可能会阻塞,直到从对等系统收到数据为止。 Berkeley Sockets 模型中的默认行为是套接字在阻止模式下运行,除非程序员显式请求将操作视为非阻止操作。 Windows 套接字 1.1 环境无法采用抢先式计划。 因此,强烈建议程序员在 Windows 套接字 1.1 中尽可能使用非阻止 (异步) 操作。 由于这并不总是可行,因此提供了下面所述的伪阻塞设施。
注意
Windows 套接字 2 仅在死锁不是问题的抢占式 32 位操作系统上运行。 Windows 套接字 1.1 建议的编程做法在 Windows 套接字 2 中不是必需的。
即使在阻塞套接字上,某些函数(例如 bind、 getockopt 和 getpeername )也会立即完成。 这些函数的阻止操作和非阻止操作之间没有区别。 其他操作(如 recv)可以立即完成,或者需要任意时间才能完成,具体取决于各种传输条件。 应用于阻塞套接字时,这些操作称为阻塞操作。 以下函数可能会阻止:
对于 16 位 Windows 套接字 1.1,无法立即完成的阻塞操作由伪阻止处理,如下所示。
服务提供程序启动操作,然后进入一个循环,在该循环中,它会调度所有 Windows 消息 (将处理器生成到另一个线程(如有必要)) ,然后检查 Windows 套接字函数的完成情况。 如果函数已完成,或者已调用 WSACancelBlockingCall ,则阻塞函数以适当的结果完成。
服务提供程序必须允许安装不处理消息的阻止挂钩函数,以避免在阻止操作未完成时重新输入消息的可能性。 最简单的此类阻塞挂钩函数将返回 FALSE。 如果 Windows 套接字 DLL 依赖于内部操作的消息,它可以在执行应用程序阻止挂钩之前执行 PeekMessage (hMyWnd...) ,以便它可以获取其消息,而不会影响系统的其余部分。
在 16 位 Windows 套接字 1.1 环境中,如果收到正在进行阻止操作的进程的 Windows 消息,则存在应用程序尝试发出另一个 Windows 套接字调用的风险。 由于难以安全地管理此条件,Windows 套接字 1.1 不支持此类应用程序行为。 不允许应用程序进行多个嵌套 Windows 套接字函数调用。 特定任务只允许一个未完成的函数调用。 唯一的例外是提供两个函数来帮助程序员在这种情况下: WSAIsBlocking 和 WSACancelBlockingCall。
可以随时调用 WSAIsBlocking 函数,以确定是否正在进行阻止的 Windows 套接字 1.1 调用。 同样,可以随时调用 WSACancelBlockingCall 函数来取消正在进行的阻止调用。 Windows 套接字函数的任何其他嵌套都失败,并出现错误 WSAEINPROGRESS。
应强调的是,此限制同时适用于阻止和非阻止操作。 对于在调用 WSAStartup 时协商版本 2.0 或更高版本的 Windows 套接字 2 应用程序,不存在对操作嵌套的限制。 操作在极少数情况下可能会嵌套,例如在 WSAAccept 条件接受回调期间,或者如果服务提供商反过来调用 Windows 套接字 2 函数。
尽管此机制足以满足简单应用程序的需求,但它无法支持更高级的应用程序( (例如,使用 MDI 模型的应用程序) )的复杂消息调度要求。 对于此类应用程序,Windows 套接字 API 包含函数 WSASetBlockingHook,它允许应用程序指定可调用的特殊例程,而不是前面讨论中所述的默认消息调度例程。
仅当以下所有条件都成立时,Windows 套接字提供程序才调用阻止挂钩:
- 例程被定义为能够阻止。
- 指定的套接字是阻塞套接字。
- 无法立即完成请求。
默认情况下,套接字设置为阻止,但具有 FIONBIO IOCTL 或 WSAAsyncSelect 函数的 ioctlsocket 函数可以将套接字设置为非阻止模式。
如果应用程序遵循以下准则,则永远不会调用阻塞挂钩,并且应用程序无需考虑阻止挂钩可能引入的重新进入问题:
- 它仅使用非阻止套接字。
- 它使用 WSAAsyncSelect 和/或 WSAAsyncGetXByY 例程,而不是 select 和 getXbyY 例程。
如果 Windows 套接字 1.1 应用程序调用异步或非阻止操作,该操作将指针指向内存对象 (缓冲区或全局变量(例如) 作为参数),则应用程序负责确保对象在整个操作过程中可供 Windows 套接字使用。 应用程序不得调用任何可能影响所涉及内存的映射或地址可行性的 Windows 函数。