Поделиться через


Функция WSASendTo (winsock2.h)

Функция WSASendTo отправляет данные в определенное место назначения с помощью перекрывающихся операций ввода-вывода, если это применимо.

Синтаксис

int WSAAPI WSASendTo(
  [in]  SOCKET                             s,
  [in]  LPWSABUF                           lpBuffers,
  [in]  DWORD                              dwBufferCount,
  [out] LPDWORD                            lpNumberOfBytesSent,
  [in]  DWORD                              dwFlags,
  [in]  const sockaddr                     *lpTo,
  [in]  int                                iTolen,
  [in]  LPWSAOVERLAPPED                    lpOverlapped,
  [in]  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

Параметры

[in] s

Дескриптор, определяющий сокет (возможно, подключенный).

[in] lpBuffers

Указатель на массив структур WSABUF . Каждая структура WSABUF содержит указатель на буфер и длину буфера в байтах. Для приложения Winsock после вызова функции WSASendTo система владеет этими буферами, и приложение может не получить к ним доступ. Этот массив должен оставаться действительным в течение операции отправки.

[in] dwBufferCount

Количество структур WSABUF в массиве lpBuffers .

[out] lpNumberOfBytesSent

Указатель на количество байтов, отправленных этим вызовом, если операция ввода-вывода завершается немедленно.

Используйте значение NULL для этого параметра, если параметр lpOverlapped не равен NULL , чтобы избежать потенциально ошибочных результатов. Этот параметр может иметь значение NULL , только если параметр lpOverlapped не равен NULL.

[in] dwFlags

Флаги, используемые для изменения поведения вызова функции WSASendTo .

[in] lpTo

Необязательный указатель на адрес целевого сокета в структуре SOCKADDR .

[in] iTolen

Размер адреса в байтах в параметре lpTo .

[in] lpOverlapped

Указатель на структуру WSAOVERLAPPED (игнорируется для неперекрывающихся сокетов).

[in] lpCompletionRoutine

Тип: _In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE

Указатель на подпрограмму завершения, вызываемую при завершении операции отправки (игнорируется для неперекрывающихся сокетов).

Возвращаемое значение

Если ошибка не возникает и операция отправки была завершена немедленно, WSASendTo возвращает ноль. В этом случае подпрограмма завершения уже запланирована на вызов после того, как вызывающий поток перейдет в состояние предупреждения. В противном случае возвращается значение SOCKET_ERROR , а конкретный код ошибки можно получить, вызвав WSAGetLastError. Код ошибки WSA_IO_PENDING указывает, что перекрывающаяся операция была успешно инициирована и что завершение будет указано позже. Любой другой код ошибки указывает на то, что перекрывающаяся операция не была успешно инициирована и не будет никаких указаний завершения.

Код ошибки Значение
WSAEACCES
Запрошенный адрес является широковещательным, но соответствующий флаг не установлен.
WSAEADDRNOTAVAIL
Удаленный адрес не является допустимым адресом (например, ADDR_ANY).
WSAEAFNOSUPPORT
Адреса из заданного семейства адресов не могут использоваться с этим сокетом.
WSAECONNRESET
Для сокета датаграммы UDP эта ошибка указывает на то, что предыдущая операция отправки привела к сообщению ICMP "Порт недоступен".
WSAEDESTADDRREQ
Требуется адрес назначения.
WSAEFAULT
Параметры lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent или lpCompletionRoutine не являются частью адресного пространства пользователя или параметр lpTo слишком мал.
WSAEHOSTUNREACH
Сделана попытка выполнить операцию на сокете для недоступного хоста.
WSAEINPROGRESS
Выполняется блокирующий вызов Windows Sockets 1.1 или поставщик услуг по-прежнему обрабатывает функцию обратного вызова.
WSAEINTR
Блокирующий вызов Windows Socket 1.1 был отменен через WSACancelBlockingCall.
WSAEINVAL
Сокет не был привязан с помощью привязки или сокет не создан с перекрывающимся флагом.
WSAEMSGSIZE
Сокет ориентирован на сообщения, и сообщение больше максимального значения, поддерживаемого базовым транспортом.
WSAENETDOWN
Произошел сбой сетевой подсистемы.
WSAENETRESET
Для сокета датаграмм эта ошибка указывает на то, что срок жизни истек.
WSAENETUNREACH
В настоящее время сеть недоступна с этого узла.
WSAENOBUFS
Поставщик сокетов Windows сообщает о взаимоблокировке буфера.
WSAENOTCONN
Сокет не подключен (только сокеты, ориентированные на подключение).
WSAENOTSOCK
Дескриптор не является сокетом.
WSAESHUTDOWN
Сокет был выключен; невозможно выполнить WSASendTo в сокете после вызова завершения работыс параметром SD_SEND или SD_BOTH.
WSAEWOULDBLOCK
Windows NT:

Перекрывающиеся сокеты: слишком много невыполненных перекрывающихся запросов ввода-вывода. Неперекрывающиеся сокеты. Сокет помечается как неблокируемый, и операция отправки не может быть завершена немедленно.

WSANOTINITIALISED
Перед использованием этой функции должен произойти успешный вызов WSAStartup .
WSA_IO_PENDING
Перекрываемая операция была успешно инициирована, и ее завершение будет указано позже.
WSA_OPERATION_ABORTED
Перекрываемая операция была отменена из-за закрытия сокета или выполнения команды SIO_FLUSH в WSAIoctl.

Комментарии

Функция WSASendTo предоставляет расширенные функции по сравнению со стандартной функцией sendto в двух важных областях:

  • Его можно использовать в сочетании с перекрывающимися сокетами для выполнения перекрывающихся операций отправки.
  • Он позволяет указать несколько буферов отправки, что делает их применимыми к типу точечной и сборной операций ввода-вывода.
Функция WSASendTo обычно используется в сокете без подключения, заданном параметром s , для отправки датаграммы, содержащейся в одном или нескольких буферах, в определенный одноранговый сокет, определенный параметром lpTo . Даже если сокет без подключения был ранее подключен с помощью функции connect к определенному адресу, lpTo переопределяет адрес назначения только для этой датаграммы. В сокете, ориентированном на подключение, параметры lpTo и iToLen игнорируются; В этом случае WSASendTo эквивалентен WSASend.

Для перекрывающихся сокетов (созданных с помощью WSASocket с флагом WSA_FLAG_OVERLAPPED) при отправке данных используются перекрывающиеся операции ввода-вывода, если только lpOverlapped и lpCompletionRoutine не имеют значения NULL , в этом случае сокет считается неперекрытым сокетом. Когда буферы были использованы транспортом, произойдет указание завершения (вызов подпрограммы завершения или настройка объекта события). Если операция не завершается немедленно, окончательное состояние завершения извлекается с помощью процедуры завершения или WSAGetOverlappedResult.

Примечание Если открывается сокет, выполняется вызов setsockopt , а затем выполняется вызов sendto , Windows Sockets выполняет неявный вызов функции привязки .
 
Если lpOverlapped и lpCompletionRoutine имеют значение NULL, сокет в этой функции будет рассматриваться как неперекрытый сокет.

Для неперекрывающихся сокетов последние два параметра (lpOverlapped, lpCompletionRoutine) игнорируются, и WSASendTo использует ту же семантику блокировки, что и send. Данные копируются из буферов в буфер транспорта. Если сокет не блокируется и ориентирован на поток и в буфере транспорта недостаточно места, WSASendTo возвращает только часть буферов приложения. Учитывая ту же ситуацию с буфером и блокирующий сокет, WSASendTo будет блокироваться до тех пор, пока не будет занято все содержимое буфера приложения.

Если эта функция выполняется в перекрывающемся режиме, поставщик службы Winsock отвечает за запись структур WSABUF перед возвращением из этого вызова. Это позволяет приложениям создавать массивы WSABUF на основе стека, на которые указывает параметр lpBuffers .

Для сокетов, ориентированных на сообщения, необходимо соблюдать осторожность, чтобы не превышать максимальный размер сообщения базового транспорта, который можно получить, получив значение параметра сокета SO_MAX_MSG_SIZE. Если данные слишком длинные для атомарной передачи через базовый протокол, возвращается ошибка WSAEMSGSIZE и данные не передаются.

Если сокет не привязан, система присваивает ему уникальные значения, а затем помечается как привязанный.

Если сокет подключен, функцию getsockname можно использовать для определения локального IP-адреса и порта, связанных с сокетом.

Если сокет не подключен,
Функцию getockname можно использовать для определения номера локального порта, связанного с сокетом, но возвращаемый IP-адрес устанавливается в качестве подстановочного адреса для заданного протокола (например, INADDR_ANY или "0.0.0.0" для IPv4 и IN6ADDR_ANY_INIT или "::" для IPv6).

Успешное завершение WSASendTo не означает, что данные были успешно доставлены.

Параметр dwFlags можно использовать для влияния на поведение вызова функции за пределами параметров, указанных для связанного сокета. То есть семантика этой функции определяется параметрами сокета и параметром dwFlags . Последнее создается с помощью побитового оператора OR с любым из значений, перечисленных в следующей таблице.

Значение Значение
MSG_DONTROUTE Указывает, что данные не должны подвергаться маршрутизации. Поставщик службы сокета Windows может игнорировать этот флаг.
MSG_OOB Отправка данных OOB (только сокет в стиле потока, например SOCK_STREAM ).
MSG_PARTIAL Указывает, что lpBuffers содержит только частичное сообщение. Имейте в виду, что код ошибки WSAEOPNOTSUPP будет возвращен транспортом, который не поддерживает частичную передачу сообщений.
 
Примечание При выполнении блокирующего вызова Winsock, например WSASendTo , с параметром lpOverlapped , имеющим значение NULL, Winsock может потребоваться дождаться сетевого события, прежде чем вызов сможет завершиться. В этой ситуации Winsock выполняет оповещенное ожидание, которое может быть прервано асинхронным вызовом процедуры (APC), запланированным в том же потоке. Выполнение другого блокирующего вызова Winsock внутри APC, который прервал текущий блокирующий вызов Winsock в том же потоке, приведет к неопределенному поведению и никогда не должен выполняться клиентами Winsock.
 

Перекрывающиеся ввода-вывода сокета

Если перекрывающаяся операция завершается немедленно, WSASendTo возвращает нулевое значение, а параметр lpNumberOfBytesSent обновляется числом отправленных байтов. Если перекрывающаяся операция успешно инициирована и завершится позже, WSASendTo возвращает SOCKET_ERROR и указывает код ошибки WSA_IO_PENDING. В этом случае lpNumberOfBytesSent не обновляется. После завершения перекрывающейся операции объем передаваемых данных указывается либо с помощью параметра cbTransferred в подпрограмме завершения (если указано), либо с помощью параметра lpcbTransfer в WSAGetOverlappedResult.
Примечание Все операции ввода-вывода, инициированные данным потоком, отменяются при выходе из этого потока. Для перекрывающихся сокетов ожидающие асинхронные операции могут завершиться сбоем, если поток закрывается до завершения операций. Дополнительные сведения см. в разделе ExitThread .
 
Функция WSASendTo , использующий перекрывающиеся операции ввода-вывода, может вызываться из подпрограммы завершения предыдущей функции WSARecv, WSARecvFrom, WSASend или WSASendTo . Это позволяет передавать конфиденциальные данные во времени полностью в контексте вытеснения.

Параметр lpOverlapped должен быть действителен в течение всего времени перекрывающейся операции. Если одновременно выполняется несколько операций ввода-вывода, каждая из них должна ссылаться на отдельную структуру WSAOVERLAPPED .

Если параметр lpCompletionRoutine имеет значение NULL, параметр hEventlpOverlapped получает сигнал о завершении перекрывающейся операции, если он содержит допустимый дескриптор объекта события. Приложение может использовать WSAWaitForMultipleEvents или WSAGetOverlappedResult для ожидания или опроса объекта события.

Если lpCompletionRoutine не имеет значение NULL, параметр hEvent игнорируется и может использоваться приложением для передачи контекстных сведений в подпрограмму завершения. Вызывающий объект, который передает не nulllpCompletionRoutine и более поздние версии вызывает 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 будет иметь нулевое значение. Эта функция не возвращает значение.

Возврат из этой функции позволяет использовать другую подпрограмму завершения для этого сокета. Все подпрограммы завершения ожидания вызываются до того, как оповещенный поток будет удовлетворен кодом возврата WSA_IO_COMPLETION. Подпрограммы завершения можно вызывать в любом порядке, не обязательно в том же порядке, в котором выполняются перекрывающиеся операции. Однако отправленные буферы гарантированно будут отправляться в том же порядке, в который они указаны.

Пример кода

В следующем примере демонстрируется использование функции WSASendTo с использованием объекта события .
#define WIN32_LEAN_AND_MEAN

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

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int __cdecl main(int argc, char **argv)
{

    //---------------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    WSABUF DataBuf;

    WSAOVERLAPPED Overlapped;
    SOCKET SendToSocket = INVALID_SOCKET;

    struct sockaddr_in RecvAddr;
    struct sockaddr_in LocalAddr;
    int RecvAddrSize = sizeof (RecvAddr);
    int LocalAddrSize = sizeof (LocalAddr);

    u_short Port = 27777;
    struct hostent *localHost;
    char *ip;
    
    char *targetip;
    char *targetport;

    char SendBuf[1024] = "Data buffer to send";
    int BufLen = 1024;
    DWORD BytesSent = 0;
    DWORD Flags = 0;

    int rc, err;
    int retval = 0;

    // Validate the parameters
    if (argc != 3) {
        printf("usage: %s targetip port\n", argv[0]);
        printf("  to sendto the localhost on port 27777\n");
        printf("       %s 127.0.0.1 27777\n", argv[0]);
        return 1;
    }

    targetip = argv[1];
    targetport = argv[2];

    //---------------------------------------------
    // Initialize Winsock
    // Load Winsock
    rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (rc != 0) {
        printf("Unable to load Winsock: %d\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 == WSA_INVALID_EVENT) {
        printf("WSACreateEvent failed with error: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //---------------------------------------------
    // Create a socket for sending data
    SendToSocket =
        WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
                  WSA_FLAG_OVERLAPPED);
    if (SendToSocket == INVALID_SOCKET) {
        printf("socket failed with error: %d\n", WSAGetLastError());
        WSACloseEvent(Overlapped.hEvent);
        WSACleanup();
        return 1;
    }
    //---------------------------------------------
    // Set up the RecvAddr structure with the IP address of
    // the receiver (in this example case "123.123.123.1")
    // and the specified port number.
    RecvAddr.sin_family = AF_INET;

    RecvAddr.sin_addr.s_addr = inet_addr(targetip);
    if (RecvAddr.sin_addr.s_addr == INADDR_NONE)  {
        printf("The target ip address entered must be a legal IPv4 address\n");
        WSACloseEvent(Overlapped.hEvent);
        WSACleanup();
        return 1;
    }
    RecvAddr.sin_port = htons( (u_short) atoi(targetport));
    if(RecvAddr.sin_port == 0) {
        printf("The targetport must be a legal UDP port number\n");
        WSACloseEvent(Overlapped.hEvent);
        WSACleanup();
        return 1;
    }

    //---------------------------------------------
    // Set up the LocalAddr structure with the local IP address
    // and the specified port number.
    localHost = gethostbyname("");
    ip = inet_ntoa(*(struct in_addr *) *localHost->h_addr_list);

    LocalAddr.sin_family = AF_INET;
    LocalAddr.sin_addr.s_addr = inet_addr(ip);
    LocalAddr.sin_port = htons(Port);

    //---------------------------------------------
    // Bind the sending socket to the LocalAddr structure
    // that has the internet address family, local IP address
    // and specified port number.  
    rc = bind(SendToSocket, (struct sockaddr *) &LocalAddr, LocalAddrSize);
    if (rc == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        WSACloseEvent(Overlapped.hEvent);
        closesocket(SendToSocket);
        WSACleanup();
        return 1;
    }
    //---------------------------------------------
    // Send a datagram to the receiver
    printf("Sending datagram from IPv4 address = %s port=%d\n", 
       inet_ntoa(LocalAddr.sin_addr), ntohs(LocalAddr.sin_port) ); 
    printf("   to IPv4 address = %s port=%d\n", 
       inet_ntoa(RecvAddr.sin_addr), ntohs(RecvAddr.sin_port) ); 

//    printf("Sending a datagram...\n");
    DataBuf.len = BufLen;
    DataBuf.buf = SendBuf;
    rc = WSASendTo(SendToSocket, &DataBuf, 1,
                   &BytesSent, Flags, (SOCKADDR *) & RecvAddr,
                   RecvAddrSize, &Overlapped, NULL);

    if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError()))) {
        printf("WSASendTo failed with error: %d\n", err);
        WSACloseEvent(Overlapped.hEvent);
        closesocket(SendToSocket);
        WSACleanup();
        return 1;
    }

    rc = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, INFINITE, TRUE);
    if (rc == WSA_WAIT_FAILED) {
        printf("WSAWaitForMultipleEvents failed with error: %d\n",
                WSAGetLastError());
        retval = 1;
    }

    rc = WSAGetOverlappedResult(SendToSocket, &Overlapped, &BytesSent,
                                FALSE, &Flags);
    if (rc == FALSE) {
        printf("WSASendTo failed with error: %d\n", WSAGetLastError());
        retval = 1;
    }
    else
        printf("Number of sent bytes = %d\n", BytesSent);
        
    //---------------------------------------------
    // When the application is finished sending, close the socket.
    printf("Finished sending. Closing socket.\n");
    WSACloseEvent(Overlapped.hEvent);
    closesocket(SendToSocket);
    printf("Exiting.\n");

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

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

См. также раздел

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSASocket

WSAWaitForMultipleEvents

Функции Winsock

Справочник по Winsock