WSARecvFrom 函式 (winsock2.h)

WSARecvFrom函式會接收資料包並儲存來源位址。

語法

int WSAAPI WSARecvFrom(
  [in]      SOCKET                             s,
  [in, out] LPWSABUF                           lpBuffers,
  [in]      DWORD                              dwBufferCount,
  [out]     LPDWORD                            lpNumberOfBytesRecvd,
  [in, out] LPDWORD                            lpFlags,
  [out]     sockaddr                           *lpFrom,
  [in, out] LPINT                              lpFromlen,
  [in]      LPWSAOVERLAPPED                    lpOverlapped,
  [in]      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

參數

[in] s

識別通訊端的描述項。

[in, out] lpBuffers

WSABUF結構的陣列指標。 每個 WSABUF 結構都包含緩衝區的指標和緩衝區的長度。

[in] dwBufferCount

lpBuffers陣列中的WSABUF結構數目。

[out] lpNumberOfBytesRecvd

如果 WSARecvFrom 作業立即完成,這個呼叫所接收位元組數目的指標。

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

[in, out] lpFlags

用來修改 WSARecvFrom 函式呼叫行為的旗標指標。 請參閱下面的<備註>。

[out] lpFrom

緩衝區的選擇性指標,會在重迭作業完成時保留來源位址。

[in, out] lpFromlen

只有在指定 lpFrom 時,才需要 「from」 緩衝區大小的指標,以位元組為單位。

[in] lpOverlapped

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

[in] lpCompletionRoutine

類型:_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE

WSARecvFrom 作業完成時呼叫的完成常式指標, (忽略未重迭的通訊端) 。

傳回值

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

錯誤碼 意義
WSAECONNRESET
執行硬式或失敗關閉的遠端部分已重設此虛擬電路。 此通訊端無法再使用,應用程式應予以關閉。 針對 UPD 資料包通訊端,此錯誤會指出先前的傳送作業導致 ICMP「無法連線」訊息。
WSAEFAULT
lpBufferslpFlagslpFromlpNumberOfBytesRecvdlpFromlenlpOverlappedlpCompletionRoutine參數並未完全包含在使用者位址空間的有效部分中:lpFrom緩衝區太小而無法容納對等位址。
WSAEINPROGRESS
封鎖的 Windows Sockets 1.1 呼叫正在進行中,或服務提供者仍在處理回呼函式。
WSAEINTR
封鎖的 Windows Socket 1.1 呼叫已透過 WSACancelBlockingCall取消。
WSAEINVAL
通訊端 尚未系結 (系結,例如) 。
WSAEMSGSIZE
訊息對於指定的緩衝區而言太大,而且 (不可靠的通訊協定,只會) 任何不符合緩衝區之訊息的尾端部分都已被捨棄。
WSAENETDOWN
網路子系統失敗。
WSAENETRESET
如果是資料包通訊端,此錯誤表示已超過存留時間。
WSAENOTCONN
通訊端未 (連線導向通訊端) 。
WSAEWOULDBLOCK
Windows NT:

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

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

備註

WSARecvFrom函式提供三個重要領域的標準recvfrom函式以上功能:

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

對於重迭的通訊端,此函式會用來張貼一或多個緩衝區,而傳入資料會在 (可能連線的) 通訊端上使用,之後應用程式指定的完成指示 (叫用事件物件) 。 如果作業未立即完成,則會透過完成常式或 WSAGetOverlappedResult擷取最終完成狀態。 此外, lpFromlpFromlen 所表示的值在完成本身表示之前不會更新。 應用程式在更新這些值之前,不得使用或干擾這些值;因此,應用程式不得使用自動 (,也就是這些參數的堆疊式) 變數。

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

對於未重迭的通訊端,封鎖語意與標準 WSARecv 函式的語意相同,並忽略 lpOverlappedlpCompletionRoutine 參數。 傳輸已接收和緩衝處理的任何資料,都會複製到使用者緩衝區。 對於目前未接收和緩衝傳輸之資料的封鎖通訊端案例,呼叫會封鎖直到收到資料為止。

緩衝區會依它們出現在 lpBuffers所指示的陣列中的順序填入,而緩衝區會封裝在一起,因此不會建立任何漏洞。

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

針對無連線通訊端類型,資料的來源位址會複製到 lpFrom所指出的緩衝區。 lpFromlen所指向的值會初始化為此緩衝區的大小,並在完成時修改,以指出儲存在該處的實際位址大小。 如先前針對重迭的通訊端所述, lpFromlpFromlen 參數在重迭 I/O 完成後才會更新。 因此,這些參數所指向的記憶體必須可供服務提供者使用,而且無法在應用程式堆疊框架上配置。 連接導向通訊端會忽略 lpFromlpFromlen 參數。

例如,針對位元組資料流程樣式通訊端 (輸入 SOCK_STREAM) ,傳入的資料會放在緩衝區中,直到:

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

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

意義
MSG_PEEK 預覽傳入的資料。 資料會複製到緩衝區,但不會從輸入佇列中移除。 此旗標僅適用于未重迭的通訊端。
MSG_OOB 處理 OOB 資料。
MSG_PARTIAL 此旗標僅適用于訊息導向通訊端。 在輸出時,此旗標表示資料是傳送者所傳輸訊息的一部分。 後續接收作業中將會傳輸訊息的其餘部分。 已清除 MSG_PARTIAL 旗標的後續接收作業表示寄件者訊息的結尾。

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

 

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

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

重迭的通訊端 I/O

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

您可以從先前WSARecv、WSARecvFromWSASendWSASendTo函式的完成常式內呼叫WSARecvFrom函式。 針對指定的通訊端,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時,才會叫用完成常式。

如果使用 IO 完成埠,且 lpCompletionRoutine 參數和 hEvent 參數為 Null,作業的結果會排程在 IO 完成埠上。 不論作業是否立即完成,所有成功的作業都會發生這種情況。

傳輸提供者可讓應用程式從通訊端 I/O 完成常式的內容中叫用傳送和接收作業,並保證針對指定的通訊端,I/O 完成常式不會巢狀化。 這可讓時間敏感的資料傳輸完全發生在先占式內容中。

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


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的傳回碼之前呼叫。 完成常式可以依任何順序呼叫,不一定以相同順序呼叫重迭作業完成。 不過,所張貼的緩衝區保證會以指定的相同順序填入。

範例程式碼

下列範例示範 如何使用 WSARecvFrom 函 式。
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

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

int __cdecl main()
{

    WSADATA wsaData;
    WSABUF DataBuf;
    WSAOVERLAPPED Overlapped;

    SOCKET RecvSocket = INVALID_SOCKET;
    struct sockaddr_in RecvAddr;
    struct sockaddr_in SenderAddr;

    int SenderAddrSize = sizeof (SenderAddr);
    u_short Port = 27015;

    char RecvBuf[1024];
    int BufLen = 1024;
    DWORD BytesRecv = 0;
    DWORD Flags = 0;

    int err = 0;
    int rc;
    int retval = 0;
    
    //-----------------------------------------------
    // Initialize Winsock
    rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (rc != 0) {
        /* Could not find a usable Winsock DLL */
        wprintf(L"WSAStartup failed with error: %ld\n", rc);
        return 1;
    }

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

    // Create an event handle and setup the overlapped structure.
    Overlapped.hEvent = WSACreateEvent();
    if (Overlapped.hEvent == NULL) {
        wprintf(L"WSACreateEvent failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //-----------------------------------------------
    // Create a receiver socket to receive datagrams
    RecvSocket = WSASocket(AF_INET,
                           SOCK_DGRAM,
                           IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);

    if (RecvSocket == INVALID_SOCKET) {
        /* Could not open a socket */
        wprintf(L"WSASocket failed with error: %ld\n", WSAGetLastError());
        WSACloseEvent(Overlapped.hEvent);
        WSACleanup();
        return 1;
    }
    //-----------------------------------------------
    // Bind the socket to any address and the specified port.
    RecvAddr.sin_family = AF_INET;
    RecvAddr.sin_port = htons(Port);
    RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    rc = bind(RecvSocket, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));
    if (rc != 0) {
        /* Bind to the socket failed */
        wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
        WSACloseEvent(Overlapped.hEvent);
        closesocket(RecvSocket);
        WSACleanup();
        return 1;
    }

    //-----------------------------------------------
    // Call the recvfrom function to receive datagrams
    // on the bound socket.
    DataBuf.len = BufLen;
    DataBuf.buf = RecvBuf;
    wprintf(L"Listening for incoming datagrams on port=%d\n", Port);
    rc = WSARecvFrom(RecvSocket,
                      &DataBuf,
                      1,
                      &BytesRecv,
                      &Flags,
                      (SOCKADDR *) & SenderAddr,
                      &SenderAddrSize, &Overlapped, NULL);

    if (rc != 0) {
        err = WSAGetLastError();
        if (err != WSA_IO_PENDING) {
            wprintf(L"WSARecvFrom failed with error: %ld\n", err);
            WSACloseEvent(Overlapped.hEvent);
            closesocket(RecvSocket);
            WSACleanup();
            return 1;
        }
        else {
            rc = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, INFINITE, TRUE);
            if (rc == WSA_WAIT_FAILED) {
                wprintf(L"WSAWaitForMultipleEvents failed with error: %d\n", WSAGetLastError());
                retval = 1;
            }

            rc = WSAGetOverlappedResult(RecvSocket, &Overlapped, &BytesRecv,
                                FALSE, &Flags);
            if (rc == FALSE) {
                wprintf(L"WSArecvFrom failed with error: %d\n", WSAGetLastError());
                retval = 1;
            }
            else
                wprintf(L"Number of received bytes = %d\n", BytesRecv);
                
            wprintf(L"Finished receiving. Closing socket.\n");
        }
        
    }
    //---------------------------------------------
    // When the application is finished receiving, close the socket.

    WSACloseEvent(Overlapped.hEvent);
    closesocket(RecvSocket);
    wprintf(L"Exiting.\n");

    //---------------------------------------------
    // Clean up and quit.
    WSACleanup();
    return (retval);
}

Windows Phone 8:Windows Phone Windows Phone 8 和更新版本上的市集應用程式支援此函式。

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

WSASend

WSASendTo

WSASocket

WSAWaitForMultipleEvents

Winsock 函式

Winsock 參考

sendto