WSAWaitForMultipleEvents 函式 (winsock2.h)

WSAWaitForMultipleEvents函式會在一或所有指定的事件物件處於訊號狀態、逾時間隔到期或 I/O 完成常式執行時傳回。

語法

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

逾時間隔,以毫秒為單位。 WSAWaitForMultipleEvents 如果逾時間隔過期,即使 不符合 fWaitAll 參數所指定的條件,也會傳回 。 如果 dwTimeout 參數為零, WSAWaitForMultipleEvents 會測試指定事件物件的狀態,並立即傳回。 如果 dwTimeoutWSA_INFINITEWSAWaitForMultipleEvents 會永遠等候;也就是說,逾時間隔永遠不會過期。

[in] fAlertable

值,指定執行緒是否處於可警示的等候狀態,讓系統可執行 I/O 完成常式。 如果 為 TRUE,執行緒會處於可警示的等候狀態, 而 WSAWaitForMultipleEvents 可以在系統執行 I/O 完成常式時傳回。 在此情況下,會傳回 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 市集應用程式支援此功能,Windows 8.1、Windows Server 2012 R2 及更新版本。

規格需求

   
最低支援的用戶端 Windows 8.1、Windows Vista [傳統型應用程式 |UWP 應用程式]
最低支援的伺服器 Windows Server 2003 [傳統型應用程式 |UWP 應用程式]
目標平台 Windows
標頭 winsock2.h
程式庫 Ws2_32.lib
Dll Ws2_32.dll

另請參閱

標準存取權限

WSACloseEvent

WSACreateEvent

WaitForMultipleObjectsEx

Winsock 函式

Winsock 參考