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 指示重叠操作已成功启动,稍后将指示完成。 任何其他错误代码都指示重叠的操作未成功启动,并且不会发生完成指示。
错误代码 | 含义 |
---|---|
执行硬性或异常关闭的远程端重置了虚拟线路。 因为套接字不可再用,应用程序应关闭套接字。 对于 UPD 数据报套接字,此错误将指示以前的发送操作导致 ICMP“端口无法访问”消息。 | |
lpBuffers、lpFlags、lpFrom、lpNumberOfBytesRecvd、lpFromlen、lpOverlapped 或 lpCompletionRoutine 参数未完全包含在用户地址空间的有效部分:lpFrom 缓冲区太小,无法容纳对等地址。 | |
阻止 Windows 套接字 1.1 调用正在进行,或者服务提供商仍在处理回调函数。 | |
阻止的 Windows 套接字 1.1 调用已通过 WSACancelBlockingCall 取消。 | |
套接字尚未 (绑定,例如) 。 | |
对于指定的缓冲区,该消息太大,对于不可靠的协议, (仅) 不适合缓冲区的消息的任何尾随部分被丢弃。 | |
网络子系统发生故障。 | |
对于数据报套接字,此错误显示生存时间已经过期。 | |
仅) (面向连接的套接字未连接套接字。 | |
Windows NT:
重叠套接字:存在过多未完成的重叠 I/O 请求。 未重叠的套接字:套接字标记为非阻止,并且无法立即完成接收操作。 |
|
在使用此函数之前,必须成功调用 WSAStartup 。 | |
已成功启动重叠操作,稍后将指示完成。 | |
由于套接字关闭,重叠的操作已取消。 |
注解
WSARecvFrom 函数在三个重要方面提供高于标准 recvfrom 函数的功能:
- 它可以与重叠套接字结合使用,以执行重叠的接收操作。
- 它允许指定多个接收缓冲区,使其适用于 I/O 的散点/收集类型。
- lpFlags 参数既是输入和输出参数,允许应用程序感知MSG_PARTIAL标志位的输出状态。 请注意,并非所有协议都支持 MSG_PARTIAL 标志位。
对于重叠套接字,此函数用于发布一个或多个缓冲区,当传入数据在可能连接的 () 套接字上可用时,传入数据将放入其中,之后应用程序指定的完成指示 (调用完成例程或事件对象的设置) 发生。 如果操作未立即完成,则通过完成例程或 WSAGetOverlappedResult 检索最终完成状态。 此外,在指示完成之前, lpFrom 和 lpFromlen 指示的值不会更新。 在更新这些值之前,应用程序不得使用这些值或干扰这些值;因此,应用程序不得对这些参数使用基于堆栈的) 变量的自动 (。
对于非重叠套接字,阻塞语义与标准 WSARecv 函数相同, lpOverlapped 和 lpCompletionRoutine 参数将被忽略。 传输已接收和缓冲的任何数据都将复制到用户缓冲区中。 对于传输当前没有接收和缓冲数据的阻塞套接字,调用将阻止,直到收到数据。
缓冲区按 它们在 lpBuffers 指示的数组中的出现顺序进行填充,并且会打包缓冲区,以便不创建任何孔。
如果此函数以重叠方式完成,Winsock 服务提供商负责在从此调用返回之前捕获 WSABUF 结构。 这使应用程序能够生成 lpBuffers 参数指向的基于堆栈的 WSABUF 数组。
对于无连接套接字类型,数据源自的地址将复制到 lpFrom 指示的缓冲区。 lpFromlen 指向的值初始化为此缓冲区的大小,并在完成时进行修改,以指示存储在那里的实际地址大小。 如前面所述,对于重叠套接字, lpFrom 和 lpFromlen 参数在重叠 I/O 完成之前不会更新。 因此,这些参数指向的内存必须可供服务提供商使用,并且不能在应用程序堆栈帧上分配。 对于面向连接的套接字,将忽略 lpFrom 和 lpFromlen 参数。
例如,对于字节流样式套接字 (键入 SOCK_STREAM) ,传入数据将放入缓冲区,直到:
- 填充缓冲区。
- 连接已关闭。
- 内部缓冲的数据已用完。
lpFlags 参数可用于影响函数调用的行为,超出为关联套接字指定的选项。 也就是说,此函数的语义由套接字选项和 lpFlags 参数确定。 后者是通过将按位 OR 运算符与下表中列出的任何值结合使用来构造的。
值 | 含义 |
---|---|
MSG_PEEK | 预览传入数据。 数据将复制到缓冲区中,但不会从输入队列中删除。 此标志仅对未重叠的套接字有效。 |
MSG_OOB | 处理 OOB 数据。 |
MSG_PARTIAL | 此标志仅适用于面向消息的套接字。 输出时,此标志指示数据是发送方传输的消息的一部分。 消息的剩余部分将在后续接收操作中传输。 清除 了MSG_PARTIAL 标志的后续接收操作指示发件人消息的结束。
作为输入参数,此标志指示即使服务提供商只接收了消息的一部分,接收操作也应完成。 |
对于面向消息的套接字,如果收到部分消息,则会在 lpFlags 参数中设置MSG_PARTIAL位。 如果收到完整的消息,则会在 lpFlags 中清除MSG_PARTIAL。 在延迟完成的情况下, lpFlags 指向的值不会更新。 指示完成时,应用程序应调用 WSAGetOverlappedResult 并检查 lpdwFlags 参数指向的标志。
重叠套接字 I/O
如果重叠操作立即完成, WSARecvFrom 将返回值零, lpNumberOfBytesRecvd 参数将更新为接收的字节数, lpFlags 参数所指向的标志位也会更新。 如果重叠操作已成功启动并稍后完成, 则 WSARecvFrom 将返回 SOCKET_ERROR 并指示 错误代码WSA_IO_PENDING。 在这种情况下, lpNumberOfBytesRecvd 和 lpFlags 不会更新。 当重叠操作完成时,通过完成例程 (中的 cbTransferred 参数(如果指定) )或通过 WSAGetOverlappedResult 中的 lcbTransfer 参数指示传输的数据量。 标志值是通过完成例程的 dwFlags 参数或检查 WSAGetOverlappedResult 的 lpdwFlags 参数获取的。可以从以前的 WSARecv、WSARecvFrom、WSASend 或 WSASendTo 函数的完成例程中调用 WSARecvFrom 函数。 对于给定的套接字,不会嵌套 I/O 完成例程。 这允许时间敏感型数据传输完全在抢占式上下文中进行。
lpOverlapped 参数在重叠操作期间必须有效。 如果多个 I/O 操作同时未完成,则每个操作都必须引用单独的 WSAOVERLAPPED 结构。
如果 lpCompletionRoutine 参数为 NULL,则当重叠操作完成时,如果 lpOverlapped 包含有效的事件对象句柄,则会向 lpOverlapped 的 hEvent 参数发出信号。 应用程序可以使用 WSAWaitForMultipleEvents 或 WSAGetOverlappedResult 等待或轮询事件对象。
如果 lpCompletionRoutine 不为 NULL,则 忽略 hEvent 参数,应用程序可以使用该参数将上下文信息传递给完成例程。 为相同的重叠 I/O 请求传递非 NULLlpCompletionRoutine 和更高版本调用 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 8 及更高版本上的 Windows Phone 应用商店应用支持此函数。
Windows 8.1 和 Windows 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 |