WSASendTo 函数 (winsock2.h)

WSASendTo 函数使用重叠的 I/O(如果适用)将数据发送到特定目标。

语法

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

lpBuffers 数组中的 WSABUF 结构数。

[out] lpNumberOfBytesSent

如果 I/O 操作立即完成,则指向此调用发送的字节数的指针。

如果 lpOverlapped 参数不是 NULL,请对此参数使用 NULL,以避免潜在的错误结果。 仅当 lpOverlapped 参数不为 NULL 时,此参数才能为 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
lpBufferslpTolpOverlappedlpNumberOfBytesSentlpCompletionRoutine 参数不是用户地址空间的一部分,或者 lpTo 参数太小。
WSAEHOSTUNREACH
套接字操作尝试访问无法访问的主机。
WSAEINPROGRESS
阻止 Windows Sockets 1.1 调用正在进行,或者服务提供程序仍在处理回调函数。
WSAEINTR
阻止的 Windows 套接字 1.1 调用已通过 WSACancelBlockingCall 取消。
WSAEINVAL
套接字尚未绑定 绑定,或者未使用重叠标志创建套接字。
WSAEMSGSIZE
套接字面向消息,消息大于基础传输支持的最大大小。
WSAENETDOWN
网络子系统失败。
WSAENETRESET
对于数据报套接字,此错误显示生存时间已经过期。
WSAENETUNREACH
此时不可以从此主机访问该网络。
WSAENOBUFS
Windows 套接字提供程序报告缓冲区死锁。
WSAENOTCONN
仅) 面向连接的套接字 (未连接套接字。
WSAENOTSOCK
:描述符不是套接字。
WSAESHUTDOWN
套接字已关闭;在调用关闭后,无法对套接字使用 WSASendTo,以及如何将 设置为 SD_SEND 或 SD_BOTH。
WSAEWOULDBLOCK
Windows NT:

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

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

注解

WSASendTo 函数在两个重要方面提供比标准 sendto 函数增强的功能:

  • 它可以与重叠的套接字结合使用,以执行重叠的发送操作。
  • 它允许指定多个发送缓冲区,使其适用于 I/O 的散点/收集类型。
WSASendTo 函数通常在 由 指定的无连接套接字上使用,用于将一个或多个缓冲区中包含的数据报发送到由 lpTo 参数标识的特定对等套接字。 即使以前已使用 连接 函数将无连接套接字连接到特定地址, lpTo 也仅覆盖该特定数据报的目标地址。 在面向连接的套接字上, 忽略 lpToiToLen 参数;在这种情况下, WSASendTo 等效于 WSASend

对于重叠套接字 (使用带有标记WSA_FLAG_OVERLAPPEDWSASocket 创建的) 发送数据时使用重叠 I/O,除非 lpOverlappedlpCompletionRoutine 均为 NULL,在这种情况下,套接字被视为未重叠的套接字。 当传输已使用缓冲区 () 时, (调用完成例程或事件对象的设置) ,就会发生完成指示。 如果操作未立即完成,则通过完成例程或 WSAGetOverlappedResult 检索最终完成状态。

注意 如果打开套接字,进行 setsockopt 调用,然后发出 sendto 调用,Windows 套接字将执行隐式 绑定 函数调用。
 
如果 lpOverlappedlpCompletionRoutine 均为 NULL,则此函数中的套接字将被视为未重叠的套接字。

对于非重叠套接字,将忽略最后两个参数 (lpOverlappedlpCompletionRoutine) ,WSASendTo 采用与 send 相同的阻塞语义。 数据从缓冲区 () 复制到传输缓冲区中。 如果套接字是非阻止且面向流的,并且传输的缓冲区中没有足够的空间, 则 WSASendTo 将返回,只使用应用程序的一部分缓冲区。 鉴于相同的缓冲区情况和阻塞套接字, WSASendTo 将阻止,直到使用应用程序的所有缓冲区内容。

如果此函数以重叠的方式完成,则 Winsock 服务提供商负责在从此调用返回之前捕获 WSABUF 结构。 这使应用程序能够生成 lpBuffers 参数指向的基于堆栈的 WSABUF 数组。

对于面向消息的套接字,必须注意不要超过基础传输的最大消息大小,这可以通过获取 套接字选项的值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 仅) (流式套接字(例如 SOCK_STREAM )发送 OOB 数据。
MSG_PARTIAL 指定 lpBuffers 仅包含部分消息。 请注意,不支持部分消息传输的传输将返回错误代码 WSAEOPNOTSUPP
 
注意 在发出阻止 Winsock 调用(例如 WSASendTolpOverlapped 参数设置为 NULL)时,Winsock 可能需要等待网络事件,然后才能完成调用。 在这种情况下,Winsock 执行可发出警报的等待, (在同一线程上计划的 APC) 异步过程调用可能会中断。 在 APC 内发出另一个阻止 Winsock 调用,该调用中断了同一线程上正在进行的阻止 Winsock 调用将导致未定义的行为,并且 Winsock 客户端绝不能尝试。
 

重叠套接字 I/O

如果重叠操作立即完成, WSASendTo 将返回值零,并且 lpNumberOfBytesSent 参数将更新为发送的字节数。 如果重叠操作已成功启动并稍后完成, 则 WSASendTo 将返回 SOCKET_ERROR 并指示 错误代码WSA_IO_PENDING。 在这种情况下, lpNumberOfBytesSent 不会更新。 当重叠操作完成时,通过完成例程 (中的 cbTransferred 参数(如果指定) )或通过 WSAGetOverlappedResult 中的 lcbTransfer 参数指示传输的数据量。
注意 当给定线程退出时,将取消由给定线程启动的所有 I/O。 对于重叠套接字,如果在操作完成之前关闭线程,挂起的异步操作可能会失败。 有关详细信息 ,请参阅 ExitThread
 
可以使用重叠 I/O 的 WSASendTo 函数从以前的 WSARecv、WSARecvFromWSASendWSASendTo 函数的完成例程中调用。 这允许时间敏感型数据传输完全在抢占式上下文中进行。

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 时),才会调用完成例程。

传输提供程序允许应用程序从套接字 I/O 完成例程的上下文中调用发送和接收操作,并保证对于给定套接字,不会嵌套 I/O 完成例程。 这允许时间敏感型数据传输完全在抢占式上下文中进行。

完成例程的原型如下所示。


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 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

另请参阅

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSASocket

WSAWaitForMultipleEvents

Winsock 函数

Winsock 参考