Функция 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
Количество структур WSABUF в массиве lpBuffers .
[out] lpNumberOfBytesRecvd
Указатель на число (в байтах) данных, полученных этим вызовом, если операция получения завершается немедленно.
Используйте значение NULL для этого параметра, если параметр lpOverlapped не имеет значения NULL , чтобы избежать потенциально ошибочных результатов. Этот параметр может иметь значение NULL , только если параметр lpOverlapped не равен 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 . | |
Сокет не был привязан (например, с помощью bind). | |
Сообщение было слишком большим, чтобы поместиться в указанный буфер, и (только для ненадежных протоколов) все конечные части сообщения, которые не помещались в буфер, были удалены. | |
Произошел сбой сетевой подсистемы. | |
Для сокета, ориентированного на подключение, эта ошибка означает, что подключение было разорвано из-за активности действия, которое обнаружило сбой во время выполнения операции. Для сокета датаграмм эта ошибка указывает на то, что срок жизни истек. | |
Сокет не подключен. | |
Дескриптор не является сокетом. | |
MSG_OOB указано, но сокет не является типом типа SOCK_STREAM, данные OOB не поддерживаются в домене связи, связанном с этим сокетом, или сокет является однонаправленным и поддерживает только операции отправки. | |
Сокет завершен; Невозможно вызвать WSARecv в сокете после вызова завершения работыс параметром SD_RECEIVE или SD_BOTH. | |
Соединение было разорвано из-за сбоя в сети или отсутствия ответа от одноранговой системы. | |
Windows NT: Перекрывающиеся сокеты: слишком много невыполненных перекрывающихся запросов ввода-вывода. Неперекрывающиеся сокеты. Сокет помечается как неблокируемый, и операция получения не может быть завершена немедленно. |
|
Перед использованием этой функции должен быть выполнен успешный вызов WSAStartup . | |
Перекрываемая операция была успешно инициирована, а завершение будет указано позже. | |
Перекрываемая операция была отменена из-за закрытия сокета. |
Комментарии
Функция WSARecv предоставляет некоторые дополнительные функции по сравнению со стандартной функцией recv в трех важных областях:
- Его можно использовать в сочетании с перекрывающимися сокетами для выполнения перекрывающихся операций recv .
- Он позволяет указать несколько буферов приема, что делает его применимым к типу точечной и сборочной операций ввода-вывода.
- Параметр lpFlags используется как для входных данных, так и для выходных данных, что позволяет приложениям определить выходное состояние бита флага MSG_PARTIAL . Однако бит флага MSG_PARTIAL поддерживается не всеми протоколами.
Для подключенных сокетов без подключения эта функция ограничивает адреса, с которых принимаются полученные сообщения. Функция возвращает сообщения только с удаленного адреса, указанного в соединении. Сообщения с других адресов (автоматически) отклоняются.
Для перекрывающихся сокетов WSARecv используется для размещения одного или нескольких буферов, в которые будут помещены входящие данные по мере их доступности, после чего появляется указанное приложением указание завершения (вызов подпрограммы завершения или установка объекта события). Если операция не завершается немедленно, окончательное состояние завершения извлекается с помощью процедуры завершения или WSAGetOverlappedResult.
Для непереключенных сокетов семантика блокировки идентична семантике стандартной функции recv , а параметры lpOverlapped и lpCompletionRoutine игнорируются. Все данные, уже полученные и буферизируемые транспортом, будут скопированы в указанные пользовательские буферы. В случае блокирующего сокета без данных, которые в настоящее время были получены и буферизованы транспортом, вызов будет блокироваться, пока не будут получены данные. Сокеты Windows 2 не определяют стандартный механизм блокировки времени ожидания для этой функции. Для протоколов, выступающих в качестве протоколов потока байтов, стек пытается вернуть как можно больше данных в соответствии с доступным пространством буфера и объемом полученных данных. Однако получения одного байта достаточно, чтобы разблокировать вызывающий объект. Нет никакой гарантии, что будет возвращено более одного байта. Для протоколов, действующих в качестве ориентированных на сообщения, для разблокировки вызывающего объекта требуется полное сообщение.
XP1_MESSAGE_ORIENTED | XP1_PSEUDO_STREAM | MSG_PARTIAL | Действует как |
---|---|---|---|
не задан | * | * | Поток байтов |
* | Присвойте параметру | * | Поток байтов |
set | Не задано | set | Поток байтов |
set | Не задано | не задан | Ориентированные на сообщения |
Буферы заполняются в том порядке, в котором они отображаются в массиве, на который указывает lpBuffers, и буферы упаковываются таким образом, чтобы не создавались отверстия.
Если эта функция выполняется в перекрывающемся режиме, поставщик службы Winsock отвечает за запись структур WSABUF перед возвращением из этого вызова. Это позволяет приложениям создавать массивы WSABUF на основе стека, на которые указывает параметр lpBuffers .
Для сокетов в стиле потока байтов (например, тип 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 может хранить частично заполненный запрос на получение немного дольше, чтобы позволить остальным данным поступать с сегментом TCP, для которого бит push-уведомлений имеет значение 1. Этот флаг указывает TCP не удерживать запрос на получение, а немедленно завершить его. Использовать этот флаг для передачи больших блоков не рекомендуется, так как обработка частичных блоков часто не является оптимальной. Этот флаг полезен только в тех случаях, когда получение и обработка частичных данных немедленно помогает уменьшить задержку обработки. Этот флаг является указанием, а не фактической гарантией. Этот флаг поддерживается в Windows 8.1, Windows Server 2012 R2 и более поздних версий. |
MSG_WAITALL |
Запрос на получение завершится только при возникновении одного из следующих событий:
Имейте в виду, что если базовый поставщик транспорта не поддерживает MSG_WAITALL или сокет находится в режиме без блокировки, этот вызов завершится ошибкой с WSAEOPNOTSUPP. Кроме того, если MSG_WAITALL указан вместе с MSG_OOB, MSG_PEEK или MSG_PARTIAL, этот вызов завершится ошибкой wSAEOPNOTSUPP. Этот флаг не поддерживается для сокетов датаграмм и сокетов, ориентированных на сообщения. |
Для сокетов, ориентированных на сообщения, бит MSG_PARTIAL задается в параметре lpFlags , если получено частичное сообщение. Если получено полное сообщение, MSG_PARTIAL очищается в lpFlags. В случае отложенного завершения значение, на которое указывает lpFlags , не обновляется. После указания завершения приложение должно вызвать WSAGetOverlappedResult и проверить флаги, указанные параметром lpdwFlags .
Перекрывающиеся сокеты ввода-вывода
Если перекрывающаяся операция завершается немедленно, WSARecv возвращает нулевое значение, а параметр lpNumberOfBytesRecvd обновляется на количество полученных байтов, а биты флагов, указанные параметром lpFlags , также обновляются. Если перекрывающаяся операция успешно запущена и завершится позже, WSARecv возвращает SOCKET_ERROR и указывает код ошибки WSA_IO_PENDING. В этом случае lpNumberOfBytesRecvd и lpFlags не обновляются. После завершения перекрывающейся операции объем передаваемых данных указывается либо с помощью параметра cbTransferred в подпрограмме завершения (если указано), либо с помощью параметра lpcbTransfer в WSAGetOverlappedResult. Значения флагов получаются путем проверки параметра lpdwFlagsWSAGetOverlappedResult.Функция WSARecv с использованием перекрывающихся операций ввода-вывода может вызываться из подпрограммы завершения предыдущей функции WSARecv, WSARecvFrom, WSASend или WSASendTo . Для заданного сокета подпрограммы завершения ввода-вывода не будут вложенными. Это позволяет полностью выполнять передачу конфиденциальных во времени данных в контексте вытеснения.
Параметр lpOverlapped должен быть действителен в течение всего периода перекрываемой операции. Если одновременно выполняется несколько операций ввода-вывода, каждая из них должна ссылаться на отдельную структуру WSAOVERLAPPED .
Если параметр lpCompletionRoutine имеет значение NULL, параметр hEventобъекта lpOverlapped получает сигнал о завершении перекрываемой операции, если он содержит допустимый дескриптор объекта события. Приложение может использовать WSAWaitForMultipleEvents или WSAGetOverlappedResult для ожидания или опроса объекта события.
Если значение lpCompletionRoutine не равно NULL, параметр hEvent игнорируется и может использоваться приложением для передачи контекстных сведений в подпрограмму завершения. Вызывающий объект, который передает lpCompletionRoutine, отличный от NULL, а затем вызывает WSAGetOverlappedResult для того же перекрывающегося запроса ввода-вывода, может не задать для параметра fWait для этого вызова WSAGetOverlappedResult значение TRUE. В этом случае использование параметра hEvent не определено, и попытка ожидания параметра hEvent приведет к непредсказуемым результатам.
Процедура завершения соответствует тем же правилам, которые предусмотрены для процедур завершения файлового ввода-вывода Windows. Подпрограмма завершения не будет вызываться до тех пор, пока поток не будет находиться в состоянии ожидания, доступном для предупреждений, например при вызове функции WSAWaitForMultipleEvents с параметром fAlertable, равнымTRUE .
Ниже приведен прототип процедуры завершения.
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. Подпрограммы завершения можно вызывать в любом порядке, не обязательно в том же порядке, в каком выполняются перекрывающиеся операции. Однако размещенные буферы гарантированно заполняются в том же порядке, в котором они указаны.
Если вы используете порты завершения ввода-вывода, имейте в виду, что порядок вызовов WSARecv также является порядком заполнения буферов. WSARecv не следует вызывать в одном сокете одновременно из разных потоков, так как это может привести к непредсказуемому буферному порядку.
Пример кода
В следующем примере показано, как использовать функцию 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 Windows Phone 8 и более поздних версий.
Windows 8.1 и Windows Server 2012 R2: эта функция поддерживается для приложений Магазина Windows на Windows 8.1, Windows Server 2012 R2 и более поздних версий.
Требования
Минимальная версия клиента | Windows 8.1, Windows Vista [классические приложения | Приложения UWP] |
Минимальная версия сервера | Windows Server 2003 [классические приложения | Приложения UWP] |
Целевая платформа | Windows |
Header | winsock2.h |
Библиотека | Ws2_32.lib |
DLL | Ws2_32.dll |