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
lpBuffers、lpTolpOverlappedlpNumberOfBytesSentlpCompletionRoutine參數不是使用者位址空間的一部分,或lpTo參數太小。
WSAEHOSTUNREACH
已嘗試對無法連接的主機進行通訊端作業。
WSAEINPROGRESS
封鎖的 Windows Sockets 1.1 呼叫正在進行中,或者服務提供者仍在處理回呼函式。
WSAEINTR
封鎖的 Windows Socket 1.1 呼叫已透過 WSACancelBlockingCall取消。
WSAEINVAL
通訊端尚未與 系結系結,或未使用重迭旗標建立通訊端。
WSAEMSGSIZE
通訊端是訊息導向,而且訊息大於基礎傳輸所支援的最大數目。
WSAENETDOWN
網路子系統失敗。
WSAENETRESET
如果是資料包通訊端,此錯誤表示已超過存留時間。
WSAENETUNREACH
此時無法透過此主機連接網路。
WSAENOBUFS
Windows Sockets 提供者會報告緩衝區死結。
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參數所識別的特定對等通訊端。 即使先前已使用 connect 函式連接到特定位址的無連線通訊端, lpTo 仍只會覆寫該特定資料包的目的地位址。 在連線導向通訊端上,會忽略 lpToiToLen 參數;在此情況下, WSASendTo 相當於 WSASend

對於使用 WSASocket 與旗標WSA_FLAG_OVERLAPPED建立的重迭通訊端 () 傳送資料時,除非 lpOverlappedlpCompletionRoutine 都是 Null ,在此情況下,通訊端會被視為未重迭的通訊端。 當傳輸已取用緩衝區 () 時, (叫用事件) 物件的完成常式或事件物件的設定,就會發生完成指示。 如果作業未立即完成,則會透過完成常式或 WSAGetOverlappedResult擷取最終完成狀態。

注意 如果開啟通訊端, 就會進行 setockopt 呼叫,然後進行 sendto 呼叫,Windows Sockets 會執行隱含 系結 函式呼叫。
 
如果 lpOverlappedlpCompletionRoutine 都是 Null,此函式中的通訊端將會被視為非重迭的通訊端。

對於非重迭通訊端,最後兩個參數 (lpOverlappedlpCompletionRoutine) 會被忽略,而 WSASendTo 採用與 傳送相同的封鎖語意。 資料會從緩衝區 () 複製到傳輸緩衝區。 如果通訊端為非封鎖和資料流程導向,且傳輸緩衝區中沒有足夠的空間, 則 WSASendTo 只會傳回應用程式緩衝區的一部分。 假設相同的緩衝區情況和封鎖通訊端, WSASendTo 將會封鎖,直到取用所有應用程式的緩衝區內容為止。

如果此函式是以重迭的方式完成,在從這個呼叫傳回之前,Winsock 服務提供者必須負責擷取 WSABUF 結構。 這可讓應用程式建置lpBuffers參數所指向的堆疊型WSABUF陣列。

對於訊息導向通訊端,請務必小心不要超過基礎傳輸的最大訊息大小,這可以藉由取得通訊端選項的值 SO_MAX_MSG_SIZE來取得。 如果資料太長而無法透過基礎通訊協定傳遞,則會傳回 WSAEMSGSIZE 錯誤,而且不會傳輸任何資料。

如果通訊端未系結,系統會將唯一值指派給本機關聯,然後通訊端會標示為系結。

如果通訊端已連線, 則取得ockname 函式可用來判斷與通訊端相關聯的本機 IP 位址和埠。

如果通訊端未連線,則為
getsockname 函式可用來判斷與通訊端相關聯的本機埠號碼,但傳回的 IP 位址會設定為指定通訊協定的萬用字元位址 (,例如,IPv4 INADDR_ANY或 「0.0.0.0」,而 IPv6) IN6ADDR_ANY_INIT或 「::」。

WSASendTo成功完成並不表示已成功傳遞資料。

dwFlags參數可用來影響函式調用的行為,而超出針對相關聯通訊端指定的選項。 也就是說,此函式的語意取決於通訊端選項和 dwFlags 參數。 後者是使用位 OR 運算子搭配下表所列的任何值來建構。

意義
MSG_DONTROUTE 指定資料不應受限於路由。 Windows Socket 服務提供者可以選擇忽略此旗標。
MSG_OOB (資料流程樣式通訊端傳送 OOB 資料 ,例如只 SOCK_STREAM) 。
MSG_PARTIAL 指定 lpBuffers 只包含部分訊息。 請注意,不支援部分訊息傳輸的傳輸會傳回錯誤碼 WSAEOPNOTSUPP
 
注意 發出封鎖的 Winsock 呼叫,例如 WSASendTo 並將 lpOverlapped 參數設定為 Null時,Winsock 可能需要等候網路事件,才能完成呼叫。 Winsock 會在這種情況中執行可警示的等候,而非同步程序呼叫 (APC) 排程在同一個執行緒上,可能會中斷。 在 APC 內發出另一個封鎖 Winsock 呼叫,中斷相同執行緒上持續封鎖 Winsock 呼叫會導致未定義的行為,而且永遠不會由 Winsock 用戶端嘗試。
 

重迭的通訊端 I/O

如果重迭的作業立即完成, WSASendTo 會 傳回零的值,且 lpNumberOfBytesSent 參數會以傳送的位元組數目來更新。 如果重迭的作業已成功起始,且稍後將會完成, WSASendTo 會傳回 SOCKET_ERROR ,並指出錯誤碼 WSA_IO_PENDING。 在此情況下,不會更新 lpNumberOfBytesSent 。 當重迭的作業完成時,會透過完成例 (程中的cbTransferred參數來指出傳輸的資料量,如果指定) ,或透過WSAGetOverlappedResult中的lxmlTransfer參數,則表示。
注意 當該執行緒結束時,由指定執行緒起始的所有 I/O 都會取消。 對於重迭的通訊端,如果執行緒在作業完成之前關閉執行緒,擱置的非同步作業可能會失敗。 如需詳細資訊,請參閱 ExitThread
 
使用重迭 I/O 的 WSASendTo 函式可以從先前 WSARecvWSARecvFromWSASendWSASendTo 函式的完成常式內呼叫。 這可讓時間敏感的資料傳輸完全發生在先占式內容內。

lpOverlapped參數在重迭作業期間必須有效。 如果同時未完成多個 I/O 作業,每個作業都必須參考個別 的 WSAOVERLAPPED 結構。

如果lpCompletionRoutine參數為Null,當重迭的作業包含有效的事件物件控制碼時,就會發出 lpOverlappedhEvent參數訊號。 應用程式可以使用 WSAWaitForMultipleEventsWSAGetOverlappedResult 來等候或輪詢事件物件。

如果 lpCompletionRoutine 不是 Null則會忽略 hEvent 參數,並可供應用程式用來將內容資訊傳遞至完成常式。 呼叫端,將非NulllpCompletionRoutine和更新版本針對相同的重迭 I/O 要求呼叫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.1Windows Server 2012 R2:Windows 市集應用程式支援此功能,Windows 8.1、Windows Server 2012 R2 及更新版本。

規格需求

   
最低支援的用戶端 Windows 8.1、Windows Vista [傳統型應用程式 |UWP 應用程式]
最低支援的伺服器 Windows Server 2003 [傳統型應用程式 |UWP 應用程式]
目標平台 Windows
標頭 winsock2.h
程式庫 Ws2_32.lib
Dll Ws2_32.dll

另請參閱

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSASocket

WSAWaitForMultipleEvents

Winsock 函式

Winsock 參考