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 指示重叠操作已成功启动,稍后将指示完成。 任何其他错误代码指示重叠的操作未成功启动,并且不会发生完成指示。
错误代码 | 含义 |
---|---|
:请求的地址是广播地址,但相应的标志未设置。 | |
远程地址不是有效的地址 (,例如 ADDR_ANY) 。 | |
指定系列中的地址无法与此套接字一起使用。 | |
对于 UDP 数据报套接字,此错误将指示以前的发送操作导致 ICMP“端口无法访问”消息。 | |
:需要目标地址。 | |
lpBuffers、lpTo、lpOverlapped、lpNumberOfBytesSent 或 lpCompletionRoutine 参数不是用户地址空间的一部分,或者 lpTo 参数太小。 | |
套接字操作尝试访问无法访问的主机。 | |
阻止 Windows Sockets 1.1 调用正在进行,或者服务提供程序仍在处理回调函数。 | |
阻止的 Windows 套接字 1.1 调用已通过 WSACancelBlockingCall 取消。 | |
套接字尚未绑定 绑定,或者未使用重叠标志创建套接字。 | |
套接字面向消息,消息大于基础传输支持的最大大小。 | |
网络子系统失败。 | |
对于数据报套接字,此错误显示生存时间已经过期。 | |
此时不可以从此主机访问该网络。 | |
Windows 套接字提供程序报告缓冲区死锁。 | |
仅) 面向连接的套接字 (未连接套接字。 | |
:描述符不是套接字。 | |
套接字已关闭;在调用关闭后,无法对套接字使用 WSASendTo,以及如何将 设置为 SD_SEND 或 SD_BOTH。 | |
Windows NT:
重叠套接字:存在过多未完成的重叠 I/O 请求。 未重叠的套接字:套接字标记为非阻止,并且无法立即完成发送操作。 |
|
在使用此函数之前,必须成功调用 WSAStartup 。 | |
已成功启动重叠操作,稍后将指示完成。 | |
由于套接字关闭或在 WSAIoctl 中执行 SIO_FLUSH 命令,重叠的操作已取消。 |
注解
WSASendTo 函数在两个重要方面提供比标准 sendto 函数增强的功能:
- 它可以与重叠的套接字结合使用,以执行重叠的发送操作。
- 它允许指定多个发送缓冲区,使其适用于 I/O 的散点/收集类型。
对于重叠套接字 (使用带有标记WSA_FLAG_OVERLAPPED的 WSASocket 创建的) 发送数据时使用重叠 I/O,除非 lpOverlapped 和 lpCompletionRoutine 均为 NULL,在这种情况下,套接字被视为未重叠的套接字。 当传输已使用缓冲区 () 时, (调用完成例程或事件对象的设置) ,就会发生完成指示。 如果操作未立即完成,则通过完成例程或 WSAGetOverlappedResult 检索最终完成状态。
对于非重叠套接字,将忽略最后两个参数 (lpOverlapped、 lpCompletionRoutine) ,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 。 |
重叠套接字 I/O
如果重叠操作立即完成, WSASendTo 将返回值零,并且 lpNumberOfBytesSent 参数将更新为发送的字节数。 如果重叠操作已成功启动并稍后完成, 则 WSASendTo 将返回 SOCKET_ERROR 并指示 错误代码WSA_IO_PENDING。 在这种情况下, lpNumberOfBytesSent 不会更新。 当重叠操作完成时,通过完成例程 (中的 cbTransferred 参数(如果指定) )或通过 WSAGetOverlappedResult 中的 lcbTransfer 参数指示传输的数据量。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 时),才会调用完成例程。
传输提供程序允许应用程序从套接字 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.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 |