Windows 套接字 1.1 阻止例程和 EINPROGRESS
将应用程序从伯克利套接字环境移植到 Windows 环境的一个主要问题包括阻止:也就是说,调用在完成关联作之前不返回的函数。 作需要很长时间才能完成时出现问题:例如,recv 函数,该函数可能会阻止数据,直到从对等系统收到数据为止。 Berkeley Sockets 模型中的默认行为是,除非程序员显式请求将作视为非阻止,否则套接字在阻塞模式下运行。 Windows 套接字 1.1 环境无法假定抢占式计划。 因此,强烈建议程序员尽可能使用 Windows 套接字 1.1 使用非阻止(异步)作。 由于这并非总是可能的,因此提供了以下所述的伪阻塞设施。
注意
Windows 套接字 2 仅在抢占式 32 位作系统上运行,其中死锁不是问题。 Windows 套接字 1.1 建议的编程做法在 Windows 套接字 2 中不是必需的。
即使在阻塞套接字上,某些函数(绑定、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,它允许应用程序指定一个特殊例程,该例程可以调用,而不是前面讨论中所述的默认消息调度例程。
仅当以下所有内容都为 true 时,Windows 套接字提供程序才会调用阻止挂钩:
- 例程是定义为能够阻止的例程。
- 指定的套接字是阻塞套接字。
- 无法立即完成请求。
默认情况下,套接字设置为阻止,但具有 FIONBIO IOCTL 或 WSAAsyncSelect 函数的 ioctlsocket 函数可以将套接字设置为非阻止模式。
从不调用阻塞挂钩,如果应用程序遵循以下准则,则应用程序无需关注阻止挂钩可能会引入的重新加入问题:
- 它仅使用非阻止套接字。
- 它使用 WSAAsyncSelect 和/或 WSAAsyncGetXByY 例程,而不是 选择 和 getXbyY 例程。
如果 Windows Sockets 1.1 应用程序调用一个异步或非阻止作,该作采用指向内存对象(例如缓冲区或全局变量)的指针作为参数,则应用程序负责确保对象在整个作过程中可供 Windows 套接字使用。 应用程序不得调用可能影响所涉及内存的映射或地址可行性的任何 Windows 函数。