WSARecv 函式 (winsock2.h)
WSARecv函式會從連接的通訊端或系結的無連線通訊端接收資料。
語法
int WSAAPI WSARecv(
[in] SOCKET s,
[in, out] LPWSABUF lpBuffers,
[in] DWORD dwBufferCount,
[out] LPDWORD lpNumberOfBytesRecvd,
[in, out] LPDWORD lpFlags,
[in] LPWSAOVERLAPPED lpOverlapped,
[in] LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
參數
[in] s
識別已連線通訊端的描述項。
[in, out] lpBuffers
WSABUF結構的陣列指標。 每個 WSABUF 結構都包含緩衝區的指標,以及緩衝區的長度,以位元組為單位。
[in] dwBufferCount
lpBuffers陣列中的WSABUF結構數目。
[out] lpNumberOfBytesRecvd
如果接收作業立即完成,則此呼叫所接收之數位的指標,以位元組為單位。
如果lpOverlapped參數不是Null,請針對此參數使用Null,以避免發生錯誤的結果。 只有當lpOverlapped參數不是Null時,此參數才能為Null。
[in, out] lpFlags
旗標的指標,用來修改 WSARecv 函式呼叫的行為。 如需詳細資訊,請參閱<備註>一節。
[in] lpOverlapped
未重迭通訊端 () 忽略 WSAOVERLAPPED 結構的指標。
[in] lpCompletionRoutine
類型:_In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE
當接收作業完成時呼叫的完成常式指標, (忽略非重迭通訊端) 。
傳回值
如果沒有發生錯誤,而且接收作業已立即完成, WSARecv 會傳回零。 在此情況下,一旦呼叫執行緒處於可警示狀態,就會排程完成常式來呼叫。 否則,會傳回 SOCKET_ERROR 的值,而且可以呼叫 WSAGetLastError來擷取特定的錯誤碼。 錯誤碼 WSA_IO_PENDING 指出重迭的作業已成功起始,且稍後將會指出完成。 任何其他錯誤碼都表示重迭的作業未成功起始,而且不會發生任何完成指示。
錯誤碼 | 意義 |
---|---|
此虛擬電路由於逾時或其他錯誤而終止。 | |
針對資料流程通訊端,虛擬線路已由遠端重設。 此通訊端無法再使用,應用程式應予以關閉。 針對 UDP 資料包通訊端,此錯誤會指出先前的傳送作業導致 ICMP「無法連線埠」訊息。 | |
通訊端是訊息導向,而虛擬線路會由遠端正常關閉。 | |
lpBuffers參數並未完全包含在使用者位址空間的有效部分。 | |
封鎖的 Windows Sockets 1.1 呼叫正在進行中,或者服務提供者仍在處理回呼函式。 | |
WSACancelBlockingCall函式已取消 (封鎖) 呼叫。 | |
通訊端尚未系結 (,例如系 結) 。 | |
訊息太大而無法放入指定的緩衝區中,而且 (只) 未放入緩衝區之訊息的任何尾端部分已捨棄。 | |
網路子系統失敗。 | |
針對連線導向通訊端,此錯誤表示連線已因在作業進行時偵測到失敗的 保持 運作活動而中斷。 如果是資料包通訊端,此錯誤表示已超過存留時間。 | |
未連接此通訊端。 | |
描述項不是通訊端。 | |
已指定MSG_OOB ,但通訊端不是資料流程樣式,例如類型SOCK_STREAM、與此通訊端相關聯的通訊網域不支援 OOB 資料,或通訊端是單向的,而且只支援傳送作業。 | |
通訊端已關閉;在叫用關閉之後,無法在通訊端上呼叫WSARecv,以及如何設定為SD_RECEIVE或SD_BOTH。 | |
因為網路錯誤或因為對等系統無法回應,所以此連接已中斷。 | |
Windows NT: 重迭的通訊端:有太多未處理的重迭 I/O 要求。 未重迭的通訊端:通訊端標示為非封鎖,且無法立即完成接收作業。 |
|
使用此函式之前,必須先進行成功的 WSAStartup 呼叫。 | |
已成功起始重迭的作業,稍後將會指出完成。 | |
由於通訊端關閉,已取消重迭的作業。 |
備註
WSARecv函式提供一些額外的功能,相較于三個重要區域中的標準recv函式:
- 它可與重迭通訊端搭配使用,以執行重迭 的 recv 作業。
- 它允許指定多個接收緩衝區,使其適用于 I/O 的散佈/收集類型。
- lpFlags參數同時用於輸入和輸出上,讓應用程式能夠感知MSG_PARTIAL旗標位的輸出狀態。 不過,所有通訊協定都不支援 MSG_PARTIAL 旗標位。
針對連線的無連線通訊端,此函式會限制接受接收訊息的來源位址。 函式只會從連接中指定的遠端位址傳回訊息。 其他位址的訊息 (會以無訊息方式) 捨棄。
對於重迭的通訊端, WSARecv 可用來張貼一或多個將放置傳入資料的緩衝區,因為當傳入資料變成可用時,應用程式指定的完成指示 (叫用完成常式或事件物件) 的設定。 如果作業未立即完成,則會透過完成常式或 WSAGetOverlappedResult擷取最終完成狀態。
對於非重迭通訊端,封鎖語意與標準 recv 函式的語意相同,而且會忽略 lpOverlapped 和 lpCompletionRoutine 參數。 傳輸已接收和緩衝處理的任何資料,都會複製到指定的使用者緩衝區。 如果封鎖通訊端目前未收到任何資料並受到傳輸緩衝,則呼叫將會封鎖,直到收到資料為止。 Windows Sockets 2 未定義此函式的任何標準封鎖逾時機制。 對於做為位元組資料流程通訊協定的通訊協定,堆疊會嘗試盡可能根據可用的緩衝區空間和接收的資料量傳回資料量。 不過,收到單一位元組就足以解除封鎖呼叫端。 不保證會傳回超過單一位元組。 對於做為訊息導向的通訊協定,需要完整訊息才能解除封鎖呼叫端。
XP1_MESSAGE_ORIENTED | XP1_PSEUDO_STREAM | MSG_PARTIAL | 做為 |
---|---|---|---|
未設定 | * | * | 位元組資料流程 |
* | 集合 | * | 位元組資料流程 |
set | 未設定 | set | 位元組資料流程 |
set | 未設定 | 未設定 | 訊息導向 |
緩衝區會填入它們出現在 由 lpBuffers指向的陣列中的順序,並封裝緩衝區,如此就不會建立任何漏洞。
如果以重迭的方式完成此函式,Winsock 服務提供者必須負責擷取 WSABUF 結構,然後再從這個呼叫傳回。 這可讓應用程式建置由 lpBuffers參數指向的堆疊型WSABUF陣列。
例如,對於位元組資料流程樣式通訊端 (,輸入 SOCK_STREAM) ,傳入的資料會放在緩衝區中,直到填滿緩衝區、關閉連接或內部緩衝的資料耗盡為止。 不論傳入資料是否填滿所有緩衝區,重迭通訊端都會發生完成指示。
例如,對於訊息導向通訊端 (,輸入 SOCK_DGRAM) ,傳入訊息會放入緩衝區中,最多到緩衝區的大小總計,而且重迭通訊端會發生完成指示。 如果訊息大於緩衝區,則緩衝區會填入訊息的第一個部分。 如果基礎服務提供者支援 MSG_PARTIAL 功能, MSG_PARTIAL 旗標會在 lpFlags 中設定,後續接收作業將會擷取訊息的其餘部分。 如果 不支援MSG_PARTIAL ,但通訊協定可靠, WSARecv 會產生 WSAEMSGSIZE 錯誤,且後續具有較大緩衝區的接收作業可用來擷取整個訊息。 否則, (即通訊協定不可靠,且不支援 MSG_PARTIAL) 、遺失多餘的資料,而 WSARecv 會產生錯誤 WSAEMSGSIZE。
若為連線導向通訊端, WSARecv 可以使用兩種方式之一來指出虛擬線路的正常終止,取決於通訊端是位元組資料流程或訊息導向。 對於位元組資料流程,已讀取零個位元組 (,如零傳回值表示成功,而 lpNumberOfBytesRecvd 值為零,) 表示正常關閉,而且不會再讀取任何位元組。 對於訊息導向通訊端,其中通常允許零位元組訊息, 使用 WSAEDISCON 的錯誤碼來表示正常關閉。 在任何情況下, WSAECONNRESET 的傳回錯誤碼都表示發生中止關閉。
lpFlags參數可用來影響函式調用的行為,超出為相關聯通訊端指定的選項。 也就是說,此函式的語意是由通訊端選項和 lpFlags 參數所決定。 後者是使用位 OR 運算子搭配下表所列的任何值來建構。
值 | 意義 |
---|---|
MSG_PEEK |
查看傳入的資料。 資料會複製到緩衝區,但不會從輸入佇列中移除。
此旗標僅適用于未重迭的通訊端。 |
MSG_OOB | 處理 OOB 資料。 |
MSG_PARTIAL |
此旗標僅適用于訊息導向通訊端。 在輸出時,這個旗標表示指定的資料是傳送者所傳輸之訊息的一部分。 後續接收作業中將會指定訊息的其餘部分。 已清除 MSG_PARTIAL 旗標的後續接收作業表示寄件者訊息的結尾。
做為輸入參數,這個旗標表示即使傳輸提供者只收到訊息的一部分,接收作業也應該完成。 |
MSG_PUSH_IMMEDIATE |
此旗標僅適用于資料流程導向通訊端。 此旗標可讓使用資料流程通訊端的應用程式,告知傳輸提供者不要延遲部分填滿的暫止接收要求完成。 這是傳輸提供者的提示,表示應用程式願意儘快接收任何傳入資料,而不需要等待可能仍在傳輸中的資料其餘部分。 構成部分填滿擱置接收要求的內容是傳輸特定事項。
在 TCP 的情況下,這是指將傳入 TCP 區段放入接收要求資料緩衝區的情況,其中沒有任何 TCP 區段表示 PUSH 位值為 1。 在此情況下,TCP 可能會保留部分填滿的接收要求,以允許剩餘的資料抵達,並將 PUSH 位設為 1 的 TCP 區段。 此旗標會告知 TCP 不保留接收要求,而是立即完成。 不建議針對大型區塊傳輸使用此旗標,因為處理部分區塊通常不是最佳。 此旗標僅適用于立即接收和處理部分資料有助於降低處理延遲的情況。 此旗標是提示,而不是實際保證。 Windows 8.1、Windows Server 2012 R2 和更新版本支援此旗標。 |
MSG_WAITALL |
只有在發生下列其中一個事件時,接收要求才會完成:
請注意,如果基礎傳輸提供者不支援 MSG_WAITALL,或通訊端處於非封鎖模式,則此呼叫將會因 WSAEOPNOTSUPP而失敗。 此外,如果 已 指定 MSG_WAITALL以及MSG_OOB、 MSG_PEEK或 MSG_PARTIAL,則此呼叫將會失敗並產生 WSAEOPNOTSUPP。 資料包通訊端或訊息導向通訊端不支援此旗標。 |
對於訊息導向通訊端,如果收到部分訊息, MSG_PARTIAL 位就會在 lpFlags 參數中設定。 如果收到完整的訊息, MSG_PARTIAL 會在 lpFlags中清除。 在延遲完成的情況下, 不會更新 lpFlags 指向的值。 指出完成時,應用程式應該呼叫 WSAGetOverlappedResult ,並檢查 lpdwFlags 參數所指示的旗標。
重迭的通訊端 I/O
如果重迭的作業立即完成, WSARecv 會傳回零的值,而且 lpNumberOfBytesRecvd 參數會更新收到的位元組數目,而且 也會更新 lpFlags 參數所指示的旗標位。 如果已成功起始重迭的作業,且稍後會完成, WSARecv 會 傳回SOCKET_ERROR ,並指出錯誤碼 WSA_IO_PENDING。 在此情況下,不會更新 lpNumberOfBytesRecvd 和 lpFlags 。 當重迭的作業完成時,如果指定了) ,或透過WSAGetOverlappedResult中的lTransfer參數,則表示透過完成常式中的cbTransfer參數 (傳輸的資料量。 藉由檢查WSAGetOverlappedResult的lpdwFlags參數,即可取得旗標值。使用重迭 I/O 的WSARecv函式可以從先前WSARecv、WSARecvFrom、WSASend或WSASendTo函式的完成常式內呼叫。 針對指定的通訊端,I/O 完成常式不會巢狀化。 這可讓時間敏感的資料傳輸完全發生在先占式內容中。
lpOverlapped參數在重迭作業期間必須有效。 如果同時處理多個 I/O 作業,每個作業都必須參考個別 的 WSAOVERLAPPED 結構。
如果lpCompletionRoutine參數為Null,當重迭的作業包含有效的事件物件控制碼時,lpOverlapped的hEvent參數會發出訊號。 應用程式可以使用 WSAWaitForMultipleEvents 或 WSAGetOverlappedResult 來等候或輪詢事件物件。
如果 lpCompletionRoutine 不是 Null, 則會忽略 hEvent 參數,而且可由應用程式用來將內容資訊傳遞至完成常式。 將非NulllpCompletionRoutine和更新版本針對相同重迭 I/O 要求的WSAGetOverlappedResult 呼叫 WSAGetOverlappedResult的呼叫端,可能不會將該WSAGetOverlappedResult叫用的fWait參數設定為TRUE。 在此情況下, 未定義 hEvent 參數的使用方式,而嘗試等候 hEvent 參數會產生無法預期的結果。
完成常式遵循與 Windows 檔案 I/O 完成常式規定相同的規則。 除非執行緒處於可警示的等候狀態,例如在叫用fAlertable參數設為TRUE的函式WSAWaitForMultipleEvents時,才會叫用完成常式。
完成常式的原型如下所示:
void CALLBACK CompletionROUTINE(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
);
CompletionRoutine 是應用程式定義或程式庫定義函數名稱的預留位置。 dwError會指定重迭作業的完成狀態,如lpOverlapped所指出。 cbTransferred參數會指定收到的位元組數目。 dwFlags參數包含當接收作業立即完成時,會出現在lpFlags中的資訊。 此函式不會傳回值。
從此函式傳回允許叫用此通訊端的另一個擱置完成常式。 使用 WSAWaitForMultipleEvents時,所有等候完成常式都會在可警示執行緒的等候符合 WSA_IO_COMPLETION的傳回碼之前呼叫。 您可以依任何順序呼叫完成常式,不一定以相同順序呼叫重迭作業。 不過,保證已張貼的緩衝區會以指定的順序填入。
如果您使用 I/O 完成埠,請注意對 WSARecv 進行的呼叫順序也是緩衝區填入的順序。 WSARecv 不應該同時從不同的執行緒在相同的通訊端上呼叫,因為它可能會導致無法預期的緩衝區順序。
範例程式碼
下列範例示範如何在重迭的 I/O 模式中使用 WSARecv 函式。#ifndef UNICODE
#define UNICODE
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable: 4127) // Conditional expression is a constant
#define DATA_BUFSIZE 4096
int __cdecl main(int argc, char **argv)
{
WSADATA wsd;
struct addrinfo *result = NULL, *ptr = NULL, hints;
WSAOVERLAPPED RecvOverlapped;
SOCKET ConnSocket = INVALID_SOCKET;
WSABUF DataBuf;
DWORD RecvBytes, Flags;
char buffer[DATA_BUFSIZE];
int err = 0;
int rc;
if (argc != 2) {
wprintf(L"usage: %s server-name\n", argv[0]);
return 1;
}
// Load Winsock
rc = WSAStartup(MAKEWORD(2, 2), &wsd);
if (rc != 0) {
wprintf(L"Unable to load Winsock: %d\n", rc);
return 1;
}
// Make sure the hints struct is zeroed out
SecureZeroMemory((PVOID) & hints, sizeof (struct addrinfo));
// Initialize the hints to retrieve the server address for IPv4
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
rc = getaddrinfo(argv[1], "27015", &hints, &result);
if (rc != 0) {
wprintf(L"getaddrinfo failed with error: %d\n", rc);
return 1;
}
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
ConnSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (ConnSocket == INVALID_SOCKET) {
wprintf(L"socket failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
return 1;
}
rc = connect(ConnSocket, ptr->ai_addr, (int) ptr->ai_addrlen);
if (rc == SOCKET_ERROR) {
if (WSAECONNREFUSED == (err = WSAGetLastError())) {
closesocket(ConnSocket);
ConnSocket = INVALID_SOCKET;
continue;
}
wprintf(L"connect failed with error: %d\n", err);
freeaddrinfo(result);
closesocket(ConnSocket);
return 1;
}
break;
}
if (ConnSocket == INVALID_SOCKET) {
wprintf(L"Unable to establish connection with the server!\n");
freeaddrinfo(result);
return 1;
}
wprintf(L"Client connected...\n");
// Make sure the RecvOverlapped struct is zeroed out
SecureZeroMemory((PVOID) & RecvOverlapped, sizeof (WSAOVERLAPPED));
// Create an event handle and setup an overlapped structure.
RecvOverlapped.hEvent = WSACreateEvent();
if (RecvOverlapped.hEvent == NULL) {
wprintf(L"WSACreateEvent failed: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ConnSocket);
return 1;
}
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = buffer;
// Call WSARecv until the peer closes the connection
// or until an error occurs
while (1) {
Flags = 0;
rc = WSARecv(ConnSocket, &DataBuf, 1, &RecvBytes, &Flags, &RecvOverlapped, NULL);
if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != (err = WSAGetLastError()))) {
wprintf(L"WSARecv failed with error: %d\n", err);
break;
}
rc = WSAWaitForMultipleEvents(1, &RecvOverlapped.hEvent, TRUE, INFINITE, TRUE);
if (rc == WSA_WAIT_FAILED) {
wprintf(L"WSAWaitForMultipleEvents failed with error: %d\n", WSAGetLastError());
break;
}
rc = WSAGetOverlappedResult(ConnSocket, &RecvOverlapped, &RecvBytes, FALSE, &Flags);
if (rc == FALSE) {
wprintf(L"WSARecv operation failed with error: %d\n", WSAGetLastError());
break;
}
wprintf(L"Read %d bytes\n", RecvBytes);
WSAResetEvent(RecvOverlapped.hEvent);
// If 0 bytes are received, the connection was closed
if (RecvBytes == 0)
break;
}
WSACloseEvent(RecvOverlapped.hEvent);
closesocket(ConnSocket);
freeaddrinfo(result);
WSACleanup();
return 0;
}
Windows Phone 8:Windows Phone 8 和更新版本Windows Phone市集應用程式支援此函式。
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 |
標頭 | winsock2.h |
程式庫 | Ws2_32.lib |
Dll | Ws2_32.dll |