WSAWaitForMultipleEvents 函数 (winsock2.h)

当一个或所有指定的事件对象处于信号状态、超时间隔过期或 I/O 完成例程已执行时, WSAWaitForMultipleEvents 函数将返回 。

语法

DWORD WSAAPI WSAWaitForMultipleEvents(
  [in] DWORD          cEvents,
  [in] const WSAEVENT *lphEvents,
  [in] BOOL           fWaitAll,
  [in] DWORD          dwTimeout,
  [in] BOOL           fAlertable
);

参数

[in] cEvents

lphEvents 指向的数组中的事件对象句柄数。 最大事件对象句柄数 WSA_MAXIMUM_WAIT_EVENTS。 必须指定一个或多个事件。

[in] lphEvents

指向事件对象句柄数组的指针。 数组可以包含不同类型的对象的句柄。 如果将 fWaitAll 参数设置为 TRUE,则它不能包含同一句柄的多个副本。 如果在等待仍在挂起时关闭其中一个句柄,则 WSAWaitForMultipleEvents 的行为未定义。

句柄必须具有 SYNCHRONIZE 访问权限。 有关详细信息,请参阅 标准访问权限

[in] fWaitAll

一个 指定等待类型的 值。 如果 为 TRUE,则当 发出 lphEvents 数组中所有对象的状态的信号时,函数将返回 。 如果 为 FALSE,则当发出任何事件对象信号时,函数将返回 。 在后一种情况下,返回值减 去WSA_WAIT_EVENT_0 指示其状态导致函数返回的事件对象的索引。 如果在调用期间发出了多个事件对象的信号,则这是所有信号事件对象的最小索引值的已发出信号的事件对象的数组索引。

[in] dwTimeout

超时间隔(以毫秒为单位)。 如果超时间隔过期,即使不满足 fWaitAll 参数指定的条件,WSAWaitForMultipleEvents 也会返回 。 如果 dwTimeout 参数为零, 则 WSAWaitForMultipleEvents 将测试指定事件对象的状态并立即返回。 如果 dwTimeoutWSA_INFINITE则 WSAWaitForMultipleEvents 将永久等待;也就是说,超时间隔永不过期。

[in] fAlertable

一个 值,该值指定是否将线程置于可警报等待状态,以便系统可以执行 I/O 完成例程。 如果 为 TRUE,则线程处于可警报等待状态,当系统执行 I/O 完成例程时 ,WSAWaitForMultipleEvents 可以返回。 在这种情况下,返回 WSA_WAIT_IO_COMPLETION ,并且尚未发出正在等待的事件的信号。 应用程序必须再次调用 WSAWaitForMultipleEvents 函数。 如果 为 FALSE,则不会将线程置于可发出警报的等待状态,并且不会执行 I/O 完成例程。

返回值

如果 WSAWaitForMultipleEvents 函数成功,则成功时的返回值是以下值之一。

返回值 含义
WSA_WAIT_EVENT_0到 (WSA_WAIT_EVENT_0 + cEvents - 1)
如果 fWaitAll 参数为 TRUE,则返回值指示所有指定的事件对象都已发出信号。

如果 fWaitAll 参数为 FALSE,则返回值减去 WSA_WAIT_EVENT_0 指示满足等待的已发出信号的事件对象的 lphEvents 数组索引。 如果在调用期间发出了多个事件对象的信号,则返回值指示信号事件对象的 lphEvents 数组索引,其索引值是所有信号事件对象的最小索引值。

WSA_WAIT_IO_COMPLETION
等待由执行的一个或多个 I/O 完成例程结束。 等待的事件尚未发出信号。 应用程序必须再次调用 WSAWaitForMultipleEvents 函数。 仅当 fAlertable 参数为 TRUE 时,才能返回此返回值。
WSA_WAIT_TIMEOUT
已过超时间隔和 fWaitAll 参数指定的条件未得到满足。 未执行 I/O 完成例程。
 

如果 WSAWaitForMultipleEvents 函数失败,则返回值 WSA_WAIT_FAILED。 下表列出了可用于 WSAGetLastError 以获取扩展错误信息的值。

错误代码 含义
WSANOTINITIALISED 在使用此函数之前,必须成功调用 WSAStartup
WSAENETDOWN 网络子系统失败。
WSAEINPROGRESS 阻止 Windows Sockets 1.1 调用正在进行,或者服务提供程序仍在处理回调函数。
WSA_NOT_ENOUGH_MEMORY 可用内存不足,无法完成操作。
WSA_INVALID_HANDLE lphEvents 数组中的一个或多个值不是有效的事件对象句柄。
WSA_INVALID_PARAMETER cEvents 参数不包含有效的句柄计数。

注解

WSAWaitForMultipleEvents 函数确定是否满足等待条件。 如果未满足条件,调用线程将进入等待状态。 它在等待满足条件时不使用处理器时间。

WSAWaitForMultipleEvents 函数在任一个或所有指定对象处于信号状态时,或者超时间隔已过时返回 。

bWaitAll 参数为 TRUE 时,仅当所有对象的状态都设置为“已发出信号”时,才会完成等待操作。 函数不会修改指定对象的状态,直到所有对象的状态都设置为信号。

bWaitAll 参数为 FALSE 时, WSAWaitForMultipleEvents 将按从索引 0 开始的顺序检查 lphEvents 数组中的句柄,直到向其中一个对象发出信号。 如果多个对象成为信号,该函数将返回 lphEvents 数组中第一个句柄的索引,该句柄已发出其信号。

此函数还用于通过将 fAlertable 参数设置为 TRUE 来执行可发出警报的等待。 这样,当系统通过调用线程执行 I/O 完成例程时,函数就可以返回 。

线程必须处于可警报等待状态,以便系统 (异步过程调用或 APC) 执行 I/O 完成例程。 因此,如果应用程序在存在具有 I/O 完成例程且 fAlertable 参数为 FALSE 的挂起异步操作时调用 WSAWaitForMultipleEvents,则即使这些 I/O 操作已完成,也不会执行这些 I/O 完成例程。

如果 fAlertable 参数为 TRUE 且其中一个挂起的操作完成,则执行 APC, WSAWaitForMultipleEvents 将返回 WSA_IO_COMPLETION。 尚未发出挂起事件信号。 应用程序必须再次调用 WSAWaitForMultipleEvents 函数。

需要可警报等待状态而不等待任何事件对象被发出信号的应用程序应使用 Windows SleepEx 函数。

WSAWaitForMultipleEvents 的当前实现调用 WaitForMultipleObjectsEx 函数。

注意 使用直接或间接创建窗口的代码调用 WSAWaitForMultipleEvents 时,请小心。 如果线程创建任何窗口,则必须处理消息。 消息广播将发送到系统中的所有窗口。 使用 WSAWaitForMultipleEvents 且没有超时限制的线程 (dwTimeout 参数设置为 WSA_INFINITE) 可能会导致系统死锁。
 

示例代码

下面的代码示例演示如何使用 WSAWaitForMultipleEvents 函数。
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

#define DATA_BUFSIZE 4096

int main()
{
    //-----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData = { 0 };
    int iResult = 0;
    BOOL bResult = TRUE;

    WSABUF DataBuf;
    char buffer[DATA_BUFSIZE];

    DWORD EventTotal = 0;
    DWORD RecvBytes = 0;
    DWORD Flags = 0;
    DWORD BytesTransferred = 0;

    WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
    WSAOVERLAPPED AcceptOverlapped;
    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET AcceptSocket = INVALID_SOCKET;

    DWORD Index;

    //-----------------------------------------
    // Initialize Winsock
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        wprintf(L"WSAStartup failed: %d\n", iResult);
        return 1;
    }
    //-----------------------------------------
    // Create a listening socket bound to a local
    // IP address and the port specified
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"socket failed with error = %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    u_short port = 27015;
    char *ip;
    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_port = htons(port);
    hostent *thisHost;

    thisHost = gethostbyname("");
    if (thisHost == NULL) {
        wprintf(L"gethostbyname failed with error = %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list);

    service.sin_addr.s_addr = inet_addr(ip);

    //-----------------------------------------
    // Bind the listening socket to the local IP address
    // and port number
    iResult = bind(ListenSocket, (SOCKADDR *) & service, sizeof (SOCKADDR));
    if (iResult != 0) {
        wprintf(L"bind failed with error = %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    //-----------------------------------------
    // Set the socket to listen for incoming
    // connection requests
    iResult = listen(ListenSocket, 1);
    if (iResult != 0) {
        wprintf(L"listen failed with error = %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    wprintf(L"Listening...\n");

    //-----------------------------------------
    // Accept and incoming connection request
    AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"accept failed with error = %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    wprintf(L"Client Accepted...\n");

    //-----------------------------------------
    // Create an event handle and setup an overlapped structure.
    EventArray[EventTotal] = WSACreateEvent();
    if (EventArray[EventTotal] == WSA_INVALID_EVENT) {
        wprintf(L"WSACreateEvent failed with error = %d\n", WSAGetLastError());
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED));
    AcceptOverlapped.hEvent = EventArray[EventTotal];

    DataBuf.len = DATA_BUFSIZE;
    DataBuf.buf = buffer;

    EventTotal++;

    //-----------------------------------------
    // Call WSARecv to receive data into DataBuf on 
    // the accepted socket in overlapped I/O mode
    if (WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL) ==
        SOCKET_ERROR) {
        iResult = WSAGetLastError();
        if (iResult != WSA_IO_PENDING)
            wprintf(L"WSARecv failed with error = %d\n", iResult);
    }
    //-----------------------------------------
    // Process overlapped receives on the socket
    while (1) {

        //-----------------------------------------
        // Wait for the overlapped I/O call to complete
        Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

        //-----------------------------------------
        // Reset the signaled event
        bResult = WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
        if (bResult == FALSE) {
            wprintf(L"WSAResetEvent failed with error = %d\n", WSAGetLastError());
        }
        //-----------------------------------------
        // Determine the status of the overlapped event
        bResult =
            WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE,
                                   &Flags);
        if (bResult == FALSE) {
            wprintf(L"WSAGetOverlappedResult failed with error = %d\n", WSAGetLastError());
        }
        //-----------------------------------------
        // If the connection has been closed, close the accepted socket
        if (BytesTransferred == 0) {
            wprintf(L"Closing accept Socket %d\n", AcceptSocket);
            closesocket(ListenSocket);
            closesocket(AcceptSocket);
            WSACloseEvent(EventArray[Index - WSA_WAIT_EVENT_0]);
            WSACleanup();
            return 1;
        }
        //-----------------------------------------
        // If data has been received, echo the received data
        // from DataBuf back to the client
        iResult =
            WSASend(AcceptSocket, &DataBuf, 1, &RecvBytes, Flags, &AcceptOverlapped, NULL);
        if (iResult != 0) {
            wprintf(L"WSASend failed with error = %d\n", WSAGetLastError());
        }
        //-----------------------------------------         
        // Reset the changed flags and overlapped structure
        Flags = 0;
        ZeroMemory(&AcceptOverlapped, sizeof (WSAOVERLAPPED));

        AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];

        //-----------------------------------------
        // Reset the data buffer
        DataBuf.len = DATA_BUFSIZE;
        DataBuf.buf = buffer;
    }

    closesocket(ListenSocket);
    closesocket(AcceptSocket);
    WSACleanup();

    return 0;
}


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

另请参阅

标准访问权限

WSACloseEvent

WSACreateEvent

WaitForMultipleObjectsEx

Winsock 函数

Winsock 参考