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 套接字 1.1 调用正在进行,或者服务提供商仍在处理回调函数。
WSAEINTR
阻止的 Windows 套接字 1.1 调用已通过 WSACancelBlockingCall 取消。
WSAEINVAL
套接字尚未 (绑定,例如) 。
WSAEMSGSIZE
对于指定的缓冲区,该消息太大,对于不可靠的协议, (仅) 不适合缓冲区的消息的任何尾随部分被丢弃。
WSAENETDOWN
网络子系统发生故障。
WSAENETRESET
对于数据报套接字,此错误显示生存时间已经过期。
WSAENOTCONN
仅) (面向连接的套接字未连接套接字。
WSAEWOULDBLOCK
Windows NT:

重叠套接字:存在过多未完成的重叠 I/O 请求。 未重叠的套接字:套接字标记为非阻止,并且无法立即完成接收操作。

WSANOTINITIALIZED
在使用此函数之前,必须成功调用 WSAStartup
WSA_IO_PENDING
已成功启动重叠操作,稍后将指示完成。
WSA_OPERATION_ABORTED
由于套接字关闭,重叠的操作已取消。

注解

WSARecvFrom 函数在三个重要方面提供高于标准 recvfrom 函数的功能:

  • 它可以与重叠套接字结合使用,以执行重叠的接收操作。
  • 它允许指定多个接收缓冲区,使其适用于 I/O 的散点/收集类型。
  • lpFlags 参数既是输入和输出参数,允许应用程序感知MSG_PARTIAL标志位的输出状态。 请注意,并非所有协议都支持 MSG_PARTIAL 标志位。
WSARecvFrom 函数主要用于 由 指定的无连接套接字。 套接字的本地地址必须已知。 对于服务器应用程序,这通常通过 绑定显式完成。 不建议对客户端应用程序进行显式绑定。 对于使用此函数的客户端应用程序,套接字可以通过 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功能,则会在 lpFlags 中设置MSG_PARTIAL标志,后续接收操作 () 将检索消息的其余部分。 如果 不支持MSG_PARTIAL ,但协议可靠, 则 WSARecvFrom 将生成错误 WSAEMSGSIZE ,并且可以使用具有较大缓冲区的后续接收操作来检索整个消息。 否则, (即协议不可靠且不支持 MSG_PARTIAL) ,多余的数据将丢失, WSARecvFrom 将生成错误 WSAEMSGSIZE。

lpFlags 参数可用于影响函数调用的行为,超出为关联套接字指定的选项。 也就是说,此函数的语义由套接字选项和 lpFlags 参数确定。 后者是通过将按位 OR 运算符与下表中列出的任何值结合使用来构造的。

含义
MSG_PEEK 预览传入数据。 数据将复制到缓冲区中,但不会从输入队列中删除。 此标志仅对未重叠的套接字有效。
MSG_OOB 处理 OOB 数据。
MSG_PARTIAL 此标志仅适用于面向消息的套接字。 输出时,此标志指示数据是发送方传输的消息的一部分。 消息的剩余部分将在后续接收操作中传输。 清除 了MSG_PARTIAL 标志的后续接收操作指示发件人消息的结束。

作为输入参数,此标志指示即使服务提供商只接收了消息的一部分,接收操作也应完成。

 

对于面向消息的套接字,如果收到部分消息,则会在 lpFlags 参数中设置MSG_PARTIAL位。 如果收到完整的消息,则会在 lpFlags 中清除MSG_PARTIAL。 在延迟完成的情况下, lpFlags 指向的值不会更新。 指示完成时,应用程序应调用 WSAGetOverlappedResult 并检查 lpdwFlags 参数指向的标志。

注意在将 lpOverlapped 参数设置为 NULL 的情况下发出阻止的 Winsock 调用(例如 WSARecvFrom)时,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、WSARecvFromWSASend 或 WSASendTo 函数的完成例程中调用 WSARecvFrom 函数。 对于给定的套接字,不会嵌套 I/O 完成例程。 这允许时间敏感型数据传输完全在抢占式上下文中进行。

lpOverlapped 参数在重叠操作期间必须有效。 如果多个 I/O 操作同时未完成,则每个操作都必须引用单独的 WSAOVERLAPPED 结构。

如果 lpCompletionRoutine 参数为 NULL,则当重叠操作完成时,如果 lpOverlapped 包含有效的事件对象句柄,则会向 lpOverlapped 的 hEvent 参数发出信号。 应用程序可以使用 WSAWaitForMultipleEventsWSAGetOverlappedResult 等待或轮询事件对象。

如果 lpCompletionRoutine 不为 NULL,则 忽略 hEvent 参数,应用程序可以使用该参数将上下文信息传递给完成例程。 为相同的重叠 I/O 请求传递非 NULLlpCompletionRoutine 和更高版本调用 WSAGetOverlappedResult 的调用方可能不会将 WSAGetOverlappedResultfWait 参数设置为 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 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

另请参阅

WSABUF

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSAOVERLAPPED

WSASend

WSASendTo

WSASocket

WSAWaitForMultipleEvents

Winsock 函数

Winsock 参考

sendto