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 Store 應用程式支援此函式。

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
程式庫 Ws2_32.lib
Dll Ws2_32.dll

另請參閱

WSACloseEvent

WSACreateEvent

WSAGetOverlappedResult

WSASocket

WSAWaitForMultipleEvents

Winsock 函式

Winsock 參考