WSARecv 函式 (winsock2.h)

WSARecv函式會從連接的通訊端或系結的無連線通訊端接收資料。

語法

int WSAAPI WSARecv(
  [in]      SOCKET                             s,
  [in, out] LPWSABUF                           lpBuffers,
  [in]      DWORD                              dwBufferCount,
  [out]     LPDWORD                            lpNumberOfBytesRecvd,
  [in, out] LPDWORD                            lpFlags,
  [in]      LPWSAOVERLAPPED                    lpOverlapped,
  [in]      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

參數

[in] s

識別已連線通訊端的描述項。

[in, out] lpBuffers

WSABUF結構的陣列指標。 每個 WSABUF 結構都包含緩衝區的指標,以及緩衝區的長度,以位元組為單位。

[in] dwBufferCount

lpBuffers陣列中的WSABUF結構數目。

[out] lpNumberOfBytesRecvd

如果接收作業立即完成,則此呼叫所接收之數位的指標,以位元組為單位。

如果lpOverlapped參數不是Null,請針對此參數使用Null,以避免發生錯誤的結果。 只有當lpOverlapped參數不是Null時,此參數才能為Null

[in, out] lpFlags

旗標的指標,用來修改 WSARecv 函式呼叫的行為。 如需詳細資訊,請參閱<備註>一節。

[in] lpOverlapped

未重迭通訊端 () 忽略 WSAOVERLAPPED 結構的指標。

[in] lpCompletionRoutine

類型:_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE

當接收作業完成時呼叫的完成常式指標, (忽略非重迭通訊端) 。

傳回值

如果沒有發生錯誤,而且接收作業已立即完成, WSARecv 會傳回零。 在此情況下,一旦呼叫執行緒處於可警示狀態,就會排程完成常式來呼叫。 否則,會傳回 SOCKET_ERROR 的值,而且可以呼叫 WSAGetLastError來擷取特定的錯誤碼。 錯誤碼 WSA_IO_PENDING 指出重迭的作業已成功起始,且稍後將會指出完成。 任何其他錯誤碼都表示重迭的作業未成功起始,而且不會發生任何完成指示。

錯誤碼 意義
WSAECONNABORTED
此虛擬電路由於逾時或其他錯誤而終止。
WSAECONNRESET
針對資料流程通訊端,虛擬線路已由遠端重設。 此通訊端無法再使用,應用程式應予以關閉。 針對 UDP 資料包通訊端,此錯誤會指出先前的傳送作業導致 ICMP「無法連線埠」訊息。
WSAEDISCON
通訊端是訊息導向,而虛擬線路會由遠端正常關閉。
WSAEFAULT
lpBuffers參數並未完全包含在使用者位址空間的有效部分。
WSAEINPROGRESS
封鎖的 Windows Sockets 1.1 呼叫正在進行中,或者服務提供者仍在處理回呼函式。
WSAEINTR
WSACancelBlockingCall函式已取消 (封鎖) 呼叫。
WSAEINVAL
通訊端尚未系結 (,例如系 ) 。
WSAEMSGSIZE
訊息太大而無法放入指定的緩衝區中,而且 (只) 未放入緩衝區之訊息的任何尾端部分已捨棄。
WSAENETDOWN
網路子系統失敗。
WSAENETRESET
針對連線導向通訊端,此錯誤表示連線已因在作業進行時偵測到失敗的 保持 運作活動而中斷。 如果是資料包通訊端,此錯誤表示已超過存留時間。
WSAENOTCONN
未連接此通訊端。
WSAENOTSOCK
描述項不是通訊端。
WSAEOPNOTSUPP
已指定MSG_OOB ,但通訊端不是資料流程樣式,例如類型SOCK_STREAM、與此通訊端相關聯的通訊網域不支援 OOB 資料,或通訊端是單向的,而且只支援傳送作業。
WSAESHUTDOWN
通訊端已關閉;在叫用關閉之後,無法在通訊端上呼叫WSARecv,以及如何設定為SD_RECEIVESD_BOTH
WSAETIMEDOUT
因為網路錯誤或因為對等系統無法回應,所以此連接已中斷。
WSAEWOULDBLOCK

Windows NT: 重迭的通訊端:有太多未處理的重迭 I/O 要求。 未重迭的通訊端:通訊端標示為非封鎖,且無法立即完成接收作業。

WSANOTINITIALISED
使用此函式之前,必須先進行成功的 WSAStartup 呼叫。
WSA_IO_PENDING
已成功起始重迭的作業,稍後將會指出完成。
WSA_OPERATION_ABORTED
由於通訊端關閉,已取消重迭的作業。

備註

WSARecv函式提供一些額外的功能,相較于三個重要區域中的標準recv函式:

  • 它可與重迭通訊端搭配使用,以執行重迭 的 recv 作業。
  • 它允許指定多個接收緩衝區,使其適用于 I/O 的散佈/收集類型。
  • lpFlags參數同時用於輸入和輸出上,讓應用程式能夠感知MSG_PARTIAL旗標位的輸出狀態。 不過,所有通訊協定都不支援 MSG_PARTIAL 旗標位。
WSARecv函式用於連接通訊端或s參數所指定的系結無連線通訊端,並用來讀取傳入資料。 必須知道通訊端的本機位址。 對於伺服器應用程式,這通常是透過 系結 或透過 接受WSAAccept明確完成。 用戶端應用程式不建議使用明確系結。 對於用戶端應用程式,通訊端可以透過 連線WSAConnectsendtoWSASendToWSAJoinLeaf隱含地系結至本機位址。

針對連線的無連線通訊端,此函式會限制接受接收訊息的來源位址。 函式只會從連接中指定的遠端位址傳回訊息。 其他位址的訊息 (會以無訊息方式) 捨棄。

對於重迭的通訊端, WSARecv 可用來張貼一或多個將放置傳入資料的緩衝區,因為當傳入資料變成可用時,應用程式指定的完成指示 (叫用完成常式或事件物件) 的設定。 如果作業未立即完成,則會透過完成常式或 WSAGetOverlappedResult擷取最終完成狀態。

注意 當該執行緒結束時,由指定執行緒起始的所有 I/O 都會取消。 對於重迭的通訊端,如果執行緒在作業完成之前關閉執行緒,擱置的非同步作業可能會失敗。 如需詳細資訊,請參閱 ExitThread
 
如果 lpOverlappedlpCompletionRoutine 都是 Null,此函式中的通訊端將會被視為非重迭的通訊端。

對於非重迭通訊端,封鎖語意與標準 recv 函式的語意相同,而且會忽略 lpOverlappedlpCompletionRoutine 參數。 傳輸已接收和緩衝處理的任何資料,都會複製到指定的使用者緩衝區。 如果封鎖通訊端目前未收到任何資料並受到傳輸緩衝,則呼叫將會封鎖,直到收到資料為止。 Windows Sockets 2 未定義此函式的任何標準封鎖逾時機制。 對於做為位元組資料流程通訊協定的通訊協定,堆疊會嘗試盡可能根據可用的緩衝區空間和接收的資料量傳回資料量。 不過,收到單一位元組就足以解除封鎖呼叫端。 不保證會傳回超過單一位元組。 對於做為訊息導向的通訊協定,需要完整訊息才能解除封鎖呼叫端。

注意 通訊端選項 SO_RCVTIMEOSO_SNDTIMEO 僅適用于封鎖通訊端。
 
通訊協定是否做為位元組資料流程,取決於XP1_MESSAGE_ORIENTED和 XP1_PSEUDO_STREAM在其WSAPROTOCOL_INFO 結構中的設定,以及傳入此函式之MSG_PARTIAL旗標的設定, (支援) 的通訊協定。 下表列出相關的組合, (星號 (*) 表示此位的設定在此案例中並不重要) 。
XP1_MESSAGE_ORIENTED XP1_PSEUDO_STREAM MSG_PARTIAL 做為
未設定 * * 位元組資料流程
* 集合 * 位元組資料流程
set 未設定 set 位元組資料流程
set 未設定 未設定 訊息導向
 

緩衝區會填入它們出現在 由 lpBuffers指向的陣列中的順序,並封裝緩衝區,如此就不會建立任何漏洞。

如果以重迭的方式完成此函式,Winsock 服務提供者必須負責擷取 WSABUF 結構,然後再從這個呼叫傳回。 這可讓應用程式建置由 lpBuffers參數指向的堆疊型WSABUF陣列。

例如,對於位元組資料流程樣式通訊端 (,輸入 SOCK_STREAM) ,傳入的資料會放在緩衝區中,直到填滿緩衝區、關閉連接或內部緩衝的資料耗盡為止。 不論傳入資料是否填滿所有緩衝區,重迭通訊端都會發生完成指示。

例如,對於訊息導向通訊端 (,輸入 SOCK_DGRAM) ,傳入訊息會放入緩衝區中,最多到緩衝區的大小總計,而且重迭通訊端會發生完成指示。 如果訊息大於緩衝區,則緩衝區會填入訊息的第一個部分。 如果基礎服務提供者支援 MSG_PARTIAL 功能, MSG_PARTIAL 旗標會在 lpFlags 中設定,後續接收作業將會擷取訊息的其餘部分。 如果 不支援MSG_PARTIAL ,但通訊協定可靠, WSARecv 會產生 WSAEMSGSIZE 錯誤,且後續具有較大緩衝區的接收作業可用來擷取整個訊息。 否則, (即通訊協定不可靠,且不支援 MSG_PARTIAL) 、遺失多餘的資料,而 WSARecv 會產生錯誤 WSAEMSGSIZE。

若為連線導向通訊端, WSARecv 可以使用兩種方式之一來指出虛擬線路的正常終止,取決於通訊端是位元組資料流程或訊息導向。 對於位元組資料流程,已讀取零個位元組 (,如零傳回值表示成功,而 lpNumberOfBytesRecvd 值為零,) 表示正常關閉,而且不會再讀取任何位元組。 對於訊息導向通訊端,其中通常允許零位元組訊息, 使用 WSAEDISCON 的錯誤碼來表示正常關閉。 在任何情況下, WSAECONNRESET 的傳回錯誤碼都表示發生中止關閉。

lpFlags參數可用來影響函式調用的行為,超出為相關聯通訊端指定的選項。 也就是說,此函式的語意是由通訊端選項和 lpFlags 參數所決定。 後者是使用位 OR 運算子搭配下表所列的任何值來建構。

意義
MSG_PEEK 查看傳入的資料。 資料會複製到緩衝區,但不會從輸入佇列中移除。

此旗標僅適用于未重迭的通訊端。

MSG_OOB 處理 OOB 資料。
MSG_PARTIAL 此旗標僅適用于訊息導向通訊端。 在輸出時,這個旗標表示指定的資料是傳送者所傳輸之訊息的一部分。 後續接收作業中將會指定訊息的其餘部分。 已清除 MSG_PARTIAL 旗標的後續接收作業表示寄件者訊息的結尾。

做為輸入參數,這個旗標表示即使傳輸提供者只收到訊息的一部分,接收作業也應該完成。

MSG_PUSH_IMMEDIATE 此旗標僅適用于資料流程導向通訊端。 此旗標可讓使用資料流程通訊端的應用程式,告知傳輸提供者不要延遲部分填滿的暫止接收要求完成。 這是傳輸提供者的提示,表示應用程式願意儘快接收任何傳入資料,而不需要等待可能仍在傳輸中的資料其餘部分。 構成部分填滿擱置接收要求的內容是傳輸特定事項。

在 TCP 的情況下,這是指將傳入 TCP 區段放入接收要求資料緩衝區的情況,其中沒有任何 TCP 區段表示 PUSH 位值為 1。 在此情況下,TCP 可能會保留部分填滿的接收要求,以允許剩餘的資料抵達,並將 PUSH 位設為 1 的 TCP 區段。 此旗標會告知 TCP 不保留接收要求,而是立即完成。

不建議針對大型區塊傳輸使用此旗標,因為處理部分區塊通常不是最佳。 此旗標僅適用于立即接收和處理部分資料有助於降低處理延遲的情況。

此旗標是提示,而不是實際保證。

Windows 8.1、Windows Server 2012 R2 和更新版本支援此旗標。

MSG_WAITALL 只有在發生下列其中一個事件時,接收要求才會完成:
  • 呼叫端提供的緩衝區已完全滿。
  • 此連接已經關閉。
  • 要求已取消或發生錯誤。

請注意,如果基礎傳輸提供者不支援 MSG_WAITALL,或通訊端處於非封鎖模式,則此呼叫將會因 WSAEOPNOTSUPP而失敗。 此外,如果 指定 MSG_WAITALL以及MSG_OOBMSG_PEEKMSG_PARTIAL,則此呼叫將會失敗並產生 WSAEOPNOTSUPP

資料包通訊端或訊息導向通訊端不支援此旗標。

 

對於訊息導向通訊端,如果收到部分訊息, MSG_PARTIAL 位就會在 lpFlags 參數中設定。 如果收到完整的訊息, MSG_PARTIAL 會在 lpFlags中清除。 在延遲完成的情況下, 不會更新 lpFlags 指向的值。 指出完成時,應用程式應該呼叫 WSAGetOverlappedResult ,並檢查 lpdwFlags 參數所指示的旗標。

注意 發出封鎖的 Winsock 呼叫時,例如 WSARecv ,並將 lpOverlapped 參數設定為 Null,Winsock 可能需要等候網路事件,才能完成呼叫。 在此情況下,Winsock 會執行可警示的等候,而非同步程序呼叫 (APC) 排程在相同執行緒上可能會中斷。 在 APC 內發出另一個封鎖 Winsock 呼叫,中斷相同執行緒上持續封鎖 Winsock 呼叫會導致未定義的行為,而且永遠不會由 Winsock 用戶端嘗試。
 

重迭的通訊端 I/O

如果重迭的作業立即完成, WSARecv 會傳回零的值,而且 lpNumberOfBytesRecvd 參數會更新收到的位元組數目,而且 也會更新 lpFlags 參數所指示的旗標位。 如果已成功起始重迭的作業,且稍後會完成, WSARecv傳回SOCKET_ERROR ,並指出錯誤碼 WSA_IO_PENDING。 在此情況下,不會更新 lpNumberOfBytesRecvdlpFlags 。 當重迭的作業完成時,如果指定了) ,或透過WSAGetOverlappedResult中的lTransfer參數,則表示透過完成常式中的cbTransfer參數 (傳輸的資料量。 藉由檢查WSAGetOverlappedResultlpdwFlags參數,即可取得旗標值。

使用重迭 I/O 的WSARecv函式可以從先前WSARecv、WSARecvFromWSASendWSASendTo函式的完成常式內呼叫。 針對指定的通訊端,I/O 完成常式不會巢狀化。 這可讓時間敏感的資料傳輸完全發生在先占式內容中。

lpOverlapped參數在重迭作業期間必須有效。 如果同時處理多個 I/O 作業,每個作業都必須參考個別 的 WSAOVERLAPPED 結構。

如果lpCompletionRoutine參數為Null,當重迭的作業包含有效的事件物件控制碼時,lpOverlappedhEvent參數會發出訊號。 應用程式可以使用 WSAWaitForMultipleEventsWSAGetOverlappedResult 來等候或輪詢事件物件。

如果 lpCompletionRoutine 不是 Null則會忽略 hEvent 參數,而且可由應用程式用來將內容資訊傳遞至完成常式。 將非NulllpCompletionRoutine和更新版本針對相同重迭 I/O 要求的WSAGetOverlappedResult 呼叫 WSAGetOverlappedResult的呼叫端,可能不會將該WSAGetOverlappedResult叫用的fWait參數設定為TRUE。 在此情況下, 未定義 hEvent 參數的使用方式,而嘗試等候 hEvent 參數會產生無法預期的結果。

完成常式遵循與 Windows 檔案 I/O 完成常式規定相同的規則。 除非執行緒處於可警示的等候狀態,例如在叫用fAlertable參數設為TRUE的函式WSAWaitForMultipleEvents時,才會叫用完成常式。

完成常式的原型如下所示:


void CALLBACK CompletionROUTINE(
  IN DWORD dwError, 
  IN DWORD cbTransferred, 
  IN LPWSAOVERLAPPED lpOverlapped, 
  IN DWORD dwFlags
);

CompletionRoutine 是應用程式定義或程式庫定義函數名稱的預留位置。 dwError會指定重迭作業的完成狀態,如lpOverlapped所指出。 cbTransferred參數會指定收到的位元組數目。 dwFlags參數包含當接收作業立即完成時,會出現在lpFlags中的資訊。 此函式不會傳回值。

從此函式傳回允許叫用此通訊端的另一個擱置完成常式。 使用 WSAWaitForMultipleEvents時,所有等候完成常式都會在可警示執行緒的等候符合 WSA_IO_COMPLETION的傳回碼之前呼叫。 您可以依任何順序呼叫完成常式,不一定以相同順序呼叫重迭作業。 不過,保證已張貼的緩衝區會以指定的順序填入。

如果您使用 I/O 完成埠,請注意對 WSARecv 進行的呼叫順序也是緩衝區填入的順序。 WSARecv 不應該同時從不同的執行緒在相同的通訊端上呼叫,因為它可能會導致無法預期的緩衝區順序。

範例程式碼

下列範例示範如何在重迭的 I/O 模式中使用 WSARecv 函式。
#ifndef UNICODE
#define UNICODE
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

#pragma warning(disable: 4127)  // Conditional expression is a constant

#define DATA_BUFSIZE 4096

int __cdecl main(int argc, char **argv)
{
    WSADATA wsd;
    struct addrinfo *result = NULL, *ptr = NULL, hints;
    WSAOVERLAPPED RecvOverlapped;
    SOCKET ConnSocket = INVALID_SOCKET;
    WSABUF DataBuf;
    DWORD RecvBytes, Flags;
    char buffer[DATA_BUFSIZE];

    int err = 0;
    int rc;

    if (argc != 2) {
        wprintf(L"usage: %s server-name\n", argv[0]);
        return 1;
    }
    // Load Winsock
    rc = WSAStartup(MAKEWORD(2, 2), &wsd);
    if (rc != 0) {
        wprintf(L"Unable to load Winsock: %d\n", rc);
        return 1;
    }
    // Make sure the hints struct is zeroed out
    SecureZeroMemory((PVOID) & hints, sizeof (struct addrinfo));

    // Initialize the hints to retrieve the server address for IPv4
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    rc = getaddrinfo(argv[1], "27015", &hints, &result);
    if (rc != 0) {
        wprintf(L"getaddrinfo failed with error: %d\n", rc);
        return 1;
    }

    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        ConnSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (ConnSocket == INVALID_SOCKET) {
            wprintf(L"socket failed with error: %d\n", WSAGetLastError());
            freeaddrinfo(result);
            return 1;
        }

        rc = connect(ConnSocket, ptr->ai_addr, (int) ptr->ai_addrlen);
        if (rc == SOCKET_ERROR) {

            if (WSAECONNREFUSED == (err = WSAGetLastError())) {
                closesocket(ConnSocket);
                ConnSocket = INVALID_SOCKET;
                continue;
            }
            wprintf(L"connect failed with error: %d\n", err);
            freeaddrinfo(result);
            closesocket(ConnSocket);
            return 1;
        }
        break;
    }
    if (ConnSocket == INVALID_SOCKET) {
        wprintf(L"Unable to establish connection with the server!\n");
        freeaddrinfo(result);
        return 1;
    }

    wprintf(L"Client connected...\n");

    // Make sure the RecvOverlapped struct is zeroed out
    SecureZeroMemory((PVOID) & RecvOverlapped, sizeof (WSAOVERLAPPED));

    // Create an event handle and setup an overlapped structure.
    RecvOverlapped.hEvent = WSACreateEvent();
    if (RecvOverlapped.hEvent == NULL) {
        wprintf(L"WSACreateEvent failed: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ConnSocket);
        return 1;
    }

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

    // Call WSARecv until the peer closes the connection
    // or until an error occurs
    while (1) {

        Flags = 0;
        rc = WSARecv(ConnSocket, &DataBuf, 1, &RecvBytes, &Flags, &RecvOverlapped, NULL);
        if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError()))) {
            wprintf(L"WSARecv failed with error: %d\n", err);
            break;
        }

        rc = WSAWaitForMultipleEvents(1, &RecvOverlapped.hEvent, TRUE, INFINITE, TRUE);
        if (rc == WSA_WAIT_FAILED) {
            wprintf(L"WSAWaitForMultipleEvents failed with error: %d\n", WSAGetLastError());
            break;
        }

        rc = WSAGetOverlappedResult(ConnSocket, &RecvOverlapped, &RecvBytes, FALSE, &Flags);
        if (rc == FALSE) {
            wprintf(L"WSARecv operation failed with error: %d\n", WSAGetLastError());
            break;
        }

        wprintf(L"Read %d bytes\n", RecvBytes);

        WSAResetEvent(RecvOverlapped.hEvent);

        // If 0 bytes are received, the connection was closed
        if (RecvBytes == 0)
            break;
    }

    WSACloseEvent(RecvOverlapped.hEvent);
    closesocket(ConnSocket);
    freeaddrinfo(result);

    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

另請參閱

WSABUF

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSAOVERLAPPED

WSASocket

WSAWaitForMultipleEvents

Winsock 函式

Winsock 參考

recv