WSAEventSelect 函数 (winsock2.h)

WSAEventSelect 函数指定要与指定的FD_XXX网络事件集关联的事件对象。

语法

int WSAAPI WSAEventSelect(
  [in] SOCKET   s,
  [in] WSAEVENT hEventObject,
  [in] long     lNetworkEvents
);

参数

[in] s

标识套接字的描述符。

[in] hEventObject

标识要与指定FD_XXX网络事件集关联的事件对象的句柄。

[in] lNetworkEvents

一个位掩码,指定应用程序感兴趣的FD_XXX网络事件的组合。

返回值

如果应用程序的网络事件和关联的事件对象的规范成功,则返回值为零。 否则,将返回值SOCKET_ERROR,并且可以通过调用 WSAGetLastError 检索特定的错误号。

selectWSAAsyncSelect 函数一样, WSAEventSelect 将经常用于确定何时可以发出 (发送recv) 的数据传输操作,并期望立即成功。 但是,可靠的应用程序必须准备好设置事件对象的可能性,并发出立即返回 WSAEWOULDBLOCK 的 Windows 套接字调用。 例如,可以执行以下操作序列:

  • 数据通过套接字 到达;Windows 套接字设置 WSAEventSelect 事件对象。
  • 应用程序执行一些其他处理。
  • 在处理时,应用程序发出 ioctlsocket (,FIONREAD...) ,并注意到有数据可供读取。
  • 应用程序发出 recv (s,...) 来读取数据。
  • 应用程序最终等待 WSAEventSelect 中指定的事件对象,该对象会立即返回指示数据已准备好读取。
  • 应用程序发出 recv (s,...) ,失败并出现错误 WSAEWOULDBLOCK
通过将内部网络事件记录中的相应位设置为) 并向关联的事件对象发出信号,成功记录网络事件 (发生的事件,在应用程序进行函数调用以隐式方式重新启用该网络事件的设置并发出关联事件对象的信号之前,不会对该网络事件执行进一步操作。
网络事件 重新启用函数
FD_READ
recvrecvfromWSARecvWSARecvExWSARecvFrom 函数。
FD_WRITE
sendsendtoWSASendWSASendTo 函数。
FD_OOB
recvrecvfromWSARecvWSARecvExWSARecvFrom 函数。
FD_ACCEPT
acceptAcceptExWSAAccept 函数,除非返回的错误代码WSATRY_AGAIN指示条件函数返回CF_DEFER。
FD_CONNECT
无。
FD_CLOSE
无。
FD_QOS
具有命令SIO_GET_QOSWSAIoctl 函数。
FD_GROUP_QOS
保留。
FD_ROUTING_ INTERFACE_CHANGE
具有命令SIO_ROUTING_INTERFACE_CHANGEWSAIoctl 函数。
FD_ADDRESS_ LIST_CHANGE
具有命令的 WSAIoctl 函数 SIO_ADDRESS_LIST_CHANGE
 

对重新启用例程的任何调用(即使调用失败)都会导致重新启用相关网络事件和事件对象的录制和信号。

对于FD_READ、FD_OOB和FD_ACCEPT网络事件,网络事件记录和事件对象信号是级别触发的。 这意味着,如果调用了重新启用例程,并且相关网络条件在调用后仍然有效,则会记录网络事件并设置关联的事件对象。 这样,应用程序就可以由事件驱动,而不必担心某个时间到达的数据量。 考虑以下序列:

  1. 传输提供程序 在套接字 上 接收 100 字节的数据,并导致 WS2_32.DLL 记录FD_READ网络事件并设置关联的事件对象。
  2. 应用程序发出 recv (buffptr、50、0) 读取 50 个字节。
  3. 传输提供程序导致 WS2_32.DLL 记录FD_READ网络事件,并再次设置关联的事件对象,因为仍有数据要读取。
借助这些语义,应用程序不需要读取所有可用数据来响应FD_READ网络事件,只需单个 recv 即可响应每个FD_READ网络事件。

FD_QOS 事件被视为边缘触发的。 当服务质量发生更改时,消息将恰好发布一次。 在提供程序检测到服务质量的进一步更改或应用程序重新协商套接字的服务质量之前,不会再发出其他消息。

FD_ROUTING_INTERFACE_CHANGE和FD_ADDRESS_LIST_CHANGE事件也被视为边缘触发。 当应用程序通过发出 WSAIoctl相应的SIO_ROUTING_INTERFACE_CHANGESIO_ADDRESS_LIST_CHANGE 请求通知后发生更改时,将恰好发布一次消息。 在应用程序重新发出 IOCTL 之前,不会收到其他消息,并且自 IOCTL 已发出以来检测到另一个更改。

如果应用程序调用 WSAEventSelect 或调用重新启用函数时已发生网络事件,则会记录网络事件并根据需要设置关联的事件对象。 例如,请看下面的序列:

  1. 应用程序调用 listen
  2. 已收到连接请求,但尚未接受。
  3. 应用程序调用 WSAEventSelect ,指定它感兴趣的套接字FD_ACCEPT网络事件。 由于网络事件的持久性,Windows 套接字会记录FD_ACCEPT网络事件并立即设置关联的事件对象。
FD_WRITE网络事件的处理方式略有不同。 当套接字首次通过调用 connect、ConnectExWSAConnect、WSAConnectByList 或 WSAConnectByName 函数连接套接字时,或者当套接字被 acceptAcceptExWSAAccept 函数接受时,然后在发送失败且 WSAEWOULDBLOCK 且缓冲区空间变为可用后,将记录FD_WRITE网络事件。 因此,应用程序可以假设可以从第一个FD_WRITE网络事件设置开始发送,并持续到发送返回 WSAEWOULDBLOCK。 发生此类故障后,应用程序会发现,当记录FD_WRITE网络事件并设置关联的事件对象时,再次可以发送。

仅当套接字配置为单独接收 OOB 数据时,才使用FD_OOB网络事件。 如果套接字配置为接收内联的 OOB 数据,则 OOB (加速) 数据被视为正常数据,应用程序应注册兴趣,并获取FD_READ网络事件,而不是FD_OOB网络事件。 应用程序可以使用 setockoptgetsockopt for SO_OOBINLINE 选项来设置或检查处理 OOB 数据的方式。

FD_CLOSE网络事件中的错误代码指示套接字关闭是正常还是中止。 如果错误代码为零,则关闭为正常;如果错误代码为 WSAECONNRESET,则表示套接字的虚拟线路已重置。 这仅适用于面向连接的套接字,例如SOCK_STREAM。

当收到对应于套接字的虚拟线路的关闭指示时,将记录FD_CLOSE网络事件。 在 TCP 术语中,这意味着当连接进入 TIME WAIT 或 CLOSE WAIT 状态时,将记录FD_CLOSE。 这源于远程端在发送端或关闭端执行关闭。 从套接字读取所有数据后,FD_CLOSE发布。 应用程序应在收到FD_CLOSE后检查剩余数据,以避免丢失数据的可能性。 有关详细信息,请参阅有关正常关闭、挂起选项和套接字关闭和关闭函数的部分。

请注意,Windows 套接字将仅记录FD_CLOSE网络事件,以指示虚拟线路关闭。 它不会记录FD_READ网络事件来指示此条件。

当流规范中的任何参数 与套接字关联时,将记录FD_QOS或FD_GROUP_QOS网络事件。 应用程序应将 WSAIoctl 与命令 SIO_GET_QOS 结合使用,以获取套接字 当前服务质量。

当本地接口在发出此类 IOCTL 后SIO_ROUTING_INTERFACE_CHANGE更改时,将记录FD_ROUTING_INTERFACE_CHANGE网络事件。

发出 WSAIoctl 和 SIO_ADDRESS_LIST_CHANGE 后,当应用程序可以将更改绑定到的套接字的协议系列的地址列表时 ,将记录FD_ADDRESS_LIST_CHANGE 网络事件。

错误代码 含义
WSANOTINITIALIZED 在使用此函数之前,必须成功调用 WSAStartup
WSAENETDOWN 网络子系统发生故障。
WSAEINVAL 其中一个指定的参数无效,或者指定的套接字处于无效状态。
WSAEINPROGRESS 阻止 Windows 套接字 1.1 调用正在进行,或者服务提供商仍在处理回调函数。
WSAENOTSOCK :描述符不是套接字。

注解

WSAEventSelect 函数用于指定要与所选FD_XXX网络事件 lNetworkEvents 关联的事件对象 hEventObject。 为其指定事件对象的套接字由 s 参数标识。 当发生任何指定的网络事件时,将设置事件对象。

WSAEventSelect 函数的操作方式与 WSAAsyncSelect 非常相似,区别在于发生指定网络事件时执行的操作。 WSAAsyncSelect 函数会导致发布应用程序指定的 Windows 消息。 WSAEventSelect 设置关联的事件对象,并将此事件的发生记录在内部网络事件记录中。 应用程序可以使用 WSAWaitForMultipleEvents 等待或轮询事件对象,并使用 WSAEnumNetworkEvents 检索内部网络事件记录的内容,从而确定发生了哪些指定网络事件。

重置与 WSAEventSelect 函数一起使用的事件对象的状态的正确方法是将事件对象的句柄传递给 hEventObject 参数中的 WSAEnumNetworkEvents 函数。 这将重置事件对象,并以原子方式调整套接字上活动 FD 事件的状态。

WSAEventSelect 是唯一导致网络活动和错误通过 WSAEnumNetworkEvents 进行记录和检索的函数。 请参阅 selectWSAAsyncSelect 的说明,了解这些函数如何报告网络活动和错误。

无论 lNetworkEvents 的值如何,WSAEventSelect 函数都会自动将套接字 设置为非阻止模式。 若要 将套接字设置 回阻止模式,首先 需要通过调用WSAEventSelect 清除与套接字 关联的事件记录, lNetworkEvents 设置为零, hEventObject 参数设置为 NULL。 然后,可以调用 ioctlsocketWSAIoctl 将套接字重新设置为阻止模式。

lNetworkEvents 参数是使用按位 OR 运算符和以下列表中指定的任何值构造的。

含义
FD_READ 想要接收阅读就绪通知。
FD_WRITE 想要接收准备写入的通知。
FD_OOB 想要接收有关 OOB 数据到达的通知。
FD_ACCEPT 想要接收传入连接的通知。
FD_CONNECT 想要接收已完成连接或多点联接操作的通知。
FD_CLOSE 想要接收套接字关闭的通知。
FD_QOS 想要接收套接字 (QoS 更改的通知。
FD_GROUP_QOS 保留以供将来与套接字组一起使用。 想要接收套接字组 QoS 更改的通知。
FD_ROUTING_ INTERFACE_CHANGE 想要接收指定目标的路由接口更改通知。
FD_ADDRESS_ LIST_CHANGE 想要接收套接字地址系列的本地地址列表更改通知。
 

为套接字发出 WSAEventSelect 将取消同一套接字的任何以前的 WSAAsyncSelectWSAEventSelect 并清除内部网络事件记录。 例如,若要将事件对象与读取和写入网络事件相关联,应用程序必须使用FD_READ和FD_WRITE调用 WSAEventSelect ,如下所示:

rc = WSAEventSelect(s, hEventObject, FD_READ|FD_WRITE);

无法为不同的网络事件指定不同的事件对象。 以下代码将不起作用;第二个调用将取消第一个 的结果,并且只有FD_WRITE网络事件将与 hEventObject2 相关联:

rc = WSAEventSelect(s, hEventObject1, FD_READ);
rc = WSAEventSelect(s, hEventObject2, FD_WRITE); //bad

若要取消套接字上的网络事件的关联和选择, lNetworkEvents 应设置为零,在这种情况下,将忽略 hEventObject 参数。

rc = WSAEventSelect(s, hEventObject, 0);

使用 closesocket 关闭套接字还会取消在 WSAEventSelect 中为套接字指定的网络事件的关联和选择。 但是,应用程序仍必须调用 WSACloseEvent 以显式关闭事件对象并释放任何资源。

调用 accept 函数时创建的套接字与用于接受它的侦听套接字具有相同的属性。 为侦听套接字设置的任何 WSAEventSelect 关联和网络事件选择集都适用于接受的套接字。 例如,如果侦听套接字具有 hEventObject 与 FD_ACCEPT、FD_READ 和 FD_WRITE 的 WSAEventSelect 关联,则该侦听套接字上接受的任何套接字也将具有与同一 hEventObject 关联的FD_ACCEPT、FD_READ和FD_WRITE网络事件。 如果需要不同的 hEventObject 或网络事件,应用程序应调用 WSAEventSelect,传递接受的套接字和所需的新信息。

示例代码

以下示例演示如何使用 WSAEventSelect 函数。
//-------------------------
// Declare and initialize variables
SOCKET ListenSocket;
WSAEVENT NewEvent;
sockaddr_in InetAddr;

//-------------------------
// Initialize listening socket
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

//-------------------------
// Bind listening socket
InetAddr.sin_family = AF_INET;
InetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InetAddr.sin_port = htons(27015);

bind (ListenSocket, (SOCKADDR *) &InetAddr, sizeof(InetAddr));

//-------------------------
// Create new event
NewEvent = WSACreateEvent();

//-------------------------
// Associate event types FD_ACCEPT and FD_CLOSE
// with the listening socket and NewEvent
WSAEventSelect( ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE);

//----------------------
// Listen for incoming connection requests 
// on the created socket
if (listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR)
    printf("Error listening on socket.\n");

printf("Listening on socket...\n");

// Need an event handler added to handle connection requests



Windows Phone 8:Windows Phone 8 及更高版本上的 Windows Phone 应用商店应用支持此函数。

Windows 8.1Windows Server 2012 R2:Windows 8.1、Windows Server 2012 R2 及更高版本的 Windows 应用商店应用支持此函数。

要求

要求
最低受支持的客户端 Windows 8.1、Windows Vista [桌面应用 |UWP 应用]
最低受支持的服务器 Windows Server 2003 [桌面应用 | UWP 应用]
目标平台 Windows
标头 winsock2.h
Library Ws2_32.lib
DLL Ws2_32.dll

另请参阅

WSAAsyncSelect

WSACloseEvent

WSACreateEvent

WSAEnumNetworkEvents

WSAWaitForMultipleEvents

Winsock 函数

Winsock 参考

shutdown