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 指示重叠操作已成功启动,稍后将指示完成。 任何其他错误代码指示重叠的操作未成功启动,并且不会发生完成指示。
错误代码 | 含义 |
---|---|
由于超时或其他故障,虚拟线路已终止。 | |
对于流套接字,虚拟线路由远程端重置。 因为套接字不可再用,应用程序应关闭套接字。 对于 UDP 数据报套接字,此错误将指示以前的发送操作导致 ICMP“端口无法访问”消息。 | |
套接字面向消息,虚拟线路由远程端正常关闭。 | |
lpBuffers 参数未完全包含在用户地址空间的有效部分。 | |
阻止 Windows Sockets 1.1 调用正在进行,或者服务提供程序仍在处理回调函数。 | |
WSACancelBlockingCall 函数取消了 (阻止) 调用。 | |
例如,尚未绑定套接字 (绑定) 。 | |
消息太大,无法容纳到指定的缓冲区中,对于不可靠的协议, (仅) 未放入缓冲区的消息的任何尾随部分已被丢弃。 | |
网络子系统失败。 | |
对于面向连接的套接字,此错误表示连接已中断,因为 保持活动 活动检测到操作正在进行时失败。 对于数据报套接字,此错误显示生存时间已经过期。 | |
套接字未连接。 | |
:描述符不是套接字。 | |
MSG_OOB 已指定,但套接字不是流样式(如类型SOCK_STREAM),与此套接字关联的通信域中不支持 OOB 数据,或者套接字是单向的,仅支持发送操作。 | |
套接字已关闭;在调用关闭后,无法在套接字上调用 WSARecv,以及如何将 设置为 SD_RECEIVE 或 SD_BOTH。 | |
因为网络故障或对等系统无法响应,已经丢弃了连接。 | |
Windows NT: 重叠套接字:存在过多未完成的重叠 I/O 请求。 未重叠的套接字:套接字标记为非阻塞,接收操作无法立即完成。 |
|
在使用此函数之前,必须成功调用 WSAStartup 。 | |
已成功启动重叠操作,稍后将指示完成。 | |
由于套接字关闭,重叠的操作已取消。 |
注解
与标准 recv 函数相比,WSARecv 函数在三个重要方面提供了一些附加功能:
- 它可以与重叠的套接字结合使用,以执行重叠 的 recv 操作。
- 它允许指定多个接收缓冲区,使其适用于 I/O 的散点/收集类型。
- lpFlags 参数在输入时使用,并在输出时返回,使应用程序能够感知MSG_PARTIAL标志位的输出状态。 但是,并非所有协议都支持 MSG_PARTIAL 标志位。
对于连接的无连接套接字,此函数限制接收消息的地址。 函数仅返回来自连接中指定的远程地址的消息。 来自其他地址的消息 (静默) 丢弃。
对于重叠套接字, WSARecv 用于在传入数据变为可用时将放置到其中的一个或多个缓冲区,之后应用程序指定的完成指示 (调用完成例程或事件对象的设置) 。 如果操作未立即完成,则通过完成例程或 WSAGetOverlappedResult 检索最终完成状态。
对于非重叠套接字,阻塞语义与标准 recv 函数的语义相同,并且忽略 lpOverlapped 和 lpCompletionRoutine 参数。 传输已接收和缓冲的任何数据都将复制到指定的用户缓冲区中。 如果阻塞套接字当前没有数据被传输接收和缓冲,则调用将阻塞,直到收到数据。 Windows 套接字 2 未为此函数定义任何标准阻止超时机制。 对于充当字节流协议的协议,堆栈会尝试根据可用的缓冲区空间和接收的数据量返回尽可能多的数据。 但是,接收单个字节足以取消阻止调用方。 不能保证返回的字节超过一个。 对于充当面向消息的协议,需要完整消息来取消阻止调用方。
XP1_MESSAGE_ORIENTED | XP1_PSEUDO_STREAM | MSG_PARTIAL | 充当 |
---|---|---|---|
未设置 | * | * | 字节流 |
* | 设置 | * | 字节流 |
设置 | 未设置 | 设置 | 字节流 |
设置 | 未设置 | 未设置 | 面向消息 |
缓冲区按 它们出现在 lpBuffers 指向的数组中的顺序进行填充,并打包缓冲区,以便不创建任何孔。
如果此函数以重叠的方式完成,则 Winsock 服务提供商负责在从此调用返回之前捕获 WSABUF 结构。 这使应用程序能够生成 lpBuffers 参数指向的基于堆栈的 WSABUF 数组。
例如,对于字节流样式的套接字 (类型SOCK_STREAM) ,传入数据将放入缓冲区中,直到填充缓冲区、关闭连接或耗尽内部缓冲的数据。 无论传入数据是否填充所有缓冲区,重叠套接字都会发生完成指示。
对于面向消息的套接字 (例如,键入 SOCK_DGRAM) ,传入消息将放入缓冲区中,最大大小为缓冲区的总大小,重叠套接字会出现完成指示。 如果消息大于缓冲区,则缓冲区将填充消息的第一部分。 如果基础服务提供程序支持MSG_PARTIAL功能,则会在 lpFlags 中设置MSG_PARTIAL标志,后续接收操作将检索消息的其余部分。 如果 不支持MSG_PARTIAL 但协议可靠, 则 WSARecv 将生成错误 WSAEMSGSIZE ,并且可以使用具有较大缓冲区的后续接收操作来检索整个消息。 否则, (,即协议不可靠且不支持 MSG_PARTIAL) ,则多余的数据将丢失, WSARecv 将生成错误 WSAEMSGSIZE。
对于面向连接的套接字, WSARecv 可以通过两种方式之一指示虚拟线路的正常终止,具体取决于套接字是面向字节流还是面向消息。 对于字节流,已读取 0 个字节 (,由零返回值指示成功, lpNumberOfBytesRecvd 值 0) 指示正常关闭且不再读取字节。 对于通常允许零字节消息的面向消息的套接字,使用错误代码为 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_OOB、 MSG_PEEK或 MSG_PARTIAL一起指定,则此调用将失败并显示 WSAEOPNOTSUPP。 数据报套接字或面向消息的套接字不支持此标志。 |
对于面向消息的套接字,如果收到部分消息,则会在 lpFlags 参数中设置MSG_PARTIAL位。 如果收到完整的消息,则会在 lpFlags 中清除MSG_PARTIAL。 在延迟完成的情况下, 不会更新 lpFlags 指向的值。 指示完成后,应用程序应调用 WSAGetOverlappedResult 并检查 lpdwFlags 参数指示的标志。
重叠套接字 I/O
如果重叠操作立即完成, 则 WSARecv 返回零值, lpNumberOfBytesRecvd 参数将更新为收到的字节数,并且 lpFlags 参数指示的标志位也会更新。 如果已成功启动重叠操作并将稍后完成, 则 WSARecv 将返回 SOCKET_ERROR 并指示 错误代码WSA_IO_PENDING。 在这种情况下,不会更新 lpNumberOfBytesRecvd 和 lpFlags 。 重叠操作完成后,通过完成例程中的 cbTransferred 参数(如果指定 () )或通过 WSAGetOverlappedResult 中的 lcbTransferred 参数指示传输的数据量。 标志值是通过检查 WSAGetOverlappedResult 的 lpdwFlags 参数获取的。可以从以前的 WSARecv、WSARecvFrom、WSASend 或 WSASendTo 函数的完成例程中调用使用重叠 I/O 的 WSARecv 函数。 对于给定的套接字,I/O 完成例程不会嵌套。 这允许时间敏感型数据传输完全在先发制人的上下文中进行。
lpOverlapped 参数必须在重叠操作期间有效。 如果多个 I/O 操作同时未完成,则每个操作都必须引用单独的 WSAOVERLAPPED 结构。
如果 lpCompletionRoutine 参数为 NULL,则当重叠操作完成时,如果它包含有效的事件对象句柄,则会向 lpOverlapped 的 hEvent 参数发出信号。 应用程序可以使用 WSAWaitForMultipleEvents 或 WSAGetOverlappedResult 等待或轮询事件对象。
如果 lpCompletionRoutine 不为 NULL,则 忽略 hEvent 参数,应用程序可以使用该参数将上下文信息传递给完成例程。 为同一重叠 I/O 请求传递非 NULLlpCompletionRoutine 并随后调用 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.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 |