WSAAccept 函式 (winsock2.h)

WSAAccept 函式會根據條件函式的傳回值,條件式接受連線、提供服務品質流程規格,並允許傳輸連線數據。

語法

SOCKET WSAAPI WSAAccept(
  [in]      SOCKET          s,
  [out]     sockaddr        *addr,
  [in, out] LPINT           addrlen,
  [in]      LPCONDITIONPROC lpfnCondition,
  [in]      DWORD_PTR       dwCallbackData
);

參數

[in] s

描述項,識別在呼叫 接聽 函式之後接聽連線的套接字。

[out] addr

接收連線實體位址的 sockaddr 結構的選擇性指標,稱為通訊層。 addr 參數的確切格式取決於建立套接字時所建立的位址系列。

[in, out] addrlen

整數的選擇性指標,包含 addr 參數所指向之 sockaddr 結構的長度,以位元組為單位。

[in] lpfnCondition

選擇性、應用程式指定的條件函式位址,會根據傳入做為參數的呼叫端資訊做出接受/拒絕決策,並選擇性地將適當的值指派給此函式的結果參數 g ,以建立或聯結套接字群組。 如果此參數為 NULL,則不會呼叫條件函式。

[in] dwCallbackData

傳回至應用程式指定條件函式的回呼數據,做為傳遞至條件函式之 dwCallbackData 參數的值。 只有當 lpfnCondition 參數不是 NULL 時,此參數才適用。 Windows Sockets 不會解譯此參數。

傳回值

如果沒有發生錯誤, WSAAccept 會傳回 SOCKET 類型的值,這是接受套接字的描述項。 否則,會傳回INVALID_SOCKET的值,而且可以呼叫 WSAGetLastError 來擷取特定的錯誤碼。

addrlen 所參考的整數一開始包含 addr 所指向的空間量。傳回時,它會包含所傳回位址的實際長度位元組。

錯誤碼 意義
WSAEACCES
嘗試透過其訪問許可權禁止的方式來存取套接字。 如果提供的連線要求逾時或已撤銷,就會傳回此錯誤。
WSAECONNREFUSED
因為目標計算機主動拒絕連線,所以無法進行連線。 如果強制拒絕連線要求,則會傳回此錯誤,如條件函式的傳回值所指出 (CF_REJECT) 。
WSAECONNRESET
遠端主機已強制關閉一個現有連線。 已指出傳入連線的這個錯誤,但在接受呼叫之前,遠端對等已終止。
WSAEFAULT
系統在嘗試在呼叫中使用指標自變數時偵測到無效的指標位址。 addrlen 參數傳回這個錯誤太小,或者 addrlpfnCondition 不是使用者位址空間的一部分。
WSAEINTR
封鎖作業因呼叫 WSACancelBlockingCall 而中斷。 如果透過 WSACancelBlockingCall 取消封鎖的 Windows Sockets 1.1 呼叫,就會傳回此錯誤。
WSAEINPROGRESS
正在執行封鎖作業。 如果發生封鎖的 Windows Sockets 1.1 呼叫正在進行,就會傳回此錯誤。
WSAEINVAL
提供的引數無效。 如果在 WSAAccept 之前未叫用接聽、條件函式的傳回值不是有效的值,或指定套接字處於無效狀態的任何情況,就會傳回此錯誤。
WSAEMFILE
太多開啟的套接字。 如果佇列在 進入 WSAAccept 時無空,而且沒有可用的套接字描述元,就會傳回此錯誤。
WSAENETDOWN
通訊端作業遇到停止的網路。 如果網路子系統失敗,就會傳回此錯誤。
WSAENOBUFS
無法執行套接字上的作業,因為系統缺少足夠的緩衝區空間,或因為佇列已滿。 如果沒有可用的緩衝區空間,就會傳回此錯誤。
WSAENOTSOCK
嘗試在不是套接字的某個專案上執行作業。 如果傳入 s 參數的套接字描述元不是套接字,就會傳回此錯誤。
WSAEOPNOTSUPP
通訊協定系列尚未設定為系統,或不存在任何實作。 如果參考的套接字不是支援連線導向服務的型別,就會傳回此錯誤。
WSAEWOULDBLOCK
無法立即完成非封鎖套接字作業。 如果套接字標示為非封鎖且未接受任何連線,則會傳回此錯誤。
WSANOTINITIALISED
應用程式未呼叫 WSAStartup,或 WSAStartup 失敗。 使用這個函式之前,會傳回成功呼叫 WSAStartup 函式 dit 時,不會發生此錯誤。
WSATRY_AGAIN
這通常為主機名稱解析期間的暫時錯誤,表示本機伺服器未收到授權伺服器的回應。 如果接受連接要求已延遲,則會傳回此錯誤,如條件函式的傳回值所示 (CF_DEFER) 。

備註

WSAAccept 函式會擷取套接字上擱置連線佇列上的第一個連線,並根據條件函式檢查它,前提是條件函式已指定為 (,而不是 NULL) 。 如果條件函式傳回 CF_ACCEPT,WSAAccept 會建立新的套接字。 新建立的套接字具有與套接字 相同的屬性, 包括向 WSAAsyncSelectWSAEventSelect 註冊的異步事件。 如果條件函式傳回 CF_REJECT,WSAAccept 會拒絕連線要求。 條件函式會在與此函式相同的線程中執行,而且應該儘快傳回。 如果無法立即做出決策,條件函式應該會傳回CF_DEFER,以指出尚未做出任何決策,而且服務提供者不應該採取此連線要求的任何動作。 當應用程式準備好對連線要求採取動作時,它會再次叫用 WSAAccept ,並傳回CF_ACCEPT或CF_REJECT作為條件函式的傳回值。

默認模式中的套接字 (封鎖) 會封鎖,直到應用程式呼叫 WSAAccept 且佇列上沒有任何連線擱置為止。

非封鎖模式中的套接字 (封鎖) 失敗,當應用程式呼叫 WSAAccept 且佇列上沒有任何連線擱置時發生 WSAEWOULDBLOCK 錯誤。 WSAAccept 成功並傳回新的套接字句柄之後,無法再使用接受的套接字來接受任何連線。 原始套接字會保持開啟狀態,並接聽新的連線要求。

addr 參數是填入連線實體位址的結果參數,稱為通訊層。 addr 參數的確切格式是由通訊發生的位址系列所決定。 addrlen 是 value-result 參數;它一開始應該包含附加元件所指向的空間量。傳回時,它會包含所傳回位址) 以位元組為單位的實際長度 (。 此呼叫會與連線導向的套接字類型搭配使用,例如 SOCK_STREAM。 如果 addr 和/或 addrlen 等於 NULL,則不會傳回所接受套接字遠端地址的相關信息。 否則,如果成功接受連線,則會填入這兩個參數。

條件函式的原型會在頭檔中定義為 Winsock2.hLPCONDITIONPROC,如下所示。

int CALLBACK 
ConditionFunc( 
  IN     LPWSABUF    lpCallerId, 
  IN     LPWSABUF    lpCallerData, 
  IN OUT LPQOS       lpSQOS, 
  IN OUT LPQOS       lpGQOS,
  IN     LPWSABUF    lpCalleeId, 
  IN     LPWSABUF    lpCalleeData, 
  OUT    GROUP FAR * g, 	
  IN     DWORD_PTR   dwCallbackData
);

ConditionFunc 是應用程式指定回呼函式的佔位符。 實際條件函式必須位於 DLL 或應用程式模組中。 它會在模組定義檔中匯出。

lpCallerId 參數指向 WSABUF 結構,其中包含連接實體的位址,其中其 len 參數是位元組的緩衝區長度,而其 buf 參數是緩衝區的指標。 lpCallerData 是包含任何用戶數據的值參數。 這些參數中的資訊會連同連線要求一起傳送。 如果沒有呼叫端識別或呼叫端數據可用,對應的參數會是 NULL。 許多網路協定不支援連線時間呼叫端數據。 大部分的傳統網路協定都可以在連線要求時間支援呼叫端標識符資訊。 lpCallerId 所指向 WSABUF 的 buf 部分指向 sockaddrsockaddr 結構會根據其位址系列解譯, (通常是藉由將sockaddr轉換成位址系列特定的某些類型) 。

lpSQOS 參數會參考呼叫端所指定套接字FLOWSPEC 結構,每個方向各有一個,後面接著任何其他提供者特定參數。 傳送或接收流程規格值會視情況忽略任何單向套接字。 NULL 值表示沒有呼叫端提供的服務品質,而且不可能進行交涉。 非 NULLlpSQOS 指標表示要進行服務品質交涉,或提供者已準備好接受沒有交涉的服務品質要求。

lpGQOS 參數是保留的,而且應該是 NULL。 (保留給未來與套接字群組搭配使用,) 參考呼叫端所要建立之套接字群組 的 FLOWSPEC 結構,每個方向各有一個,後面接著任何其他提供者特定參數。 lpGQOSNULL 值表示沒有呼叫端指定的群組服務品質。 如果交涉發生,則可以傳回服務質量資訊。

lpCalleeId 是包含連線實體之本機地址的參數。 由 lpCalleeId 指向之 WSABUF 的 buf 部分指向 sockaddr 結構。 sockaddr 結構會根據其位址系列來解譯 (通常是藉由將sockaddr轉換成位址系列的特定類型,例如結構sockaddr_in) 。

lpCalleeData 是條件函式用來提供用戶數據給連接實體的結果參數。 lpCalleeData-len> 最初包含服務提供者配置的緩衝區長度,並由 lpCalleeData-buf> 指向。 值為零表示不支援將用戶數據傳回給呼叫端。 condition 函式應該最多將 lpCalleeData-len 位元組的數據複製到 lpCalleeData-buf>>,然後更新 lpCalleeData-len>,以指出傳送的實際位元組數目。 如果未將用戶數據傳回給呼叫端,條件函式應該會將 lpCalleeData-len> 設定為零。 所有地址和用戶數據的格式都專屬於套接字所屬的位址系列。

g 參數會在 condition 函式內指派,以指出下列任何動作:

  • 如果 g 是現有的套接字群組標識符 ,請將 新增 至此群組,前提是符合此群組設定的所有需求。
  • 如果 g = SG_UNCONSTRAINED_GROUP,請建立不受限制的套接字群組,並將 設為 第一個成員。
  • 如果 g = SG_CONSTRAINED_GROUP,請建立受限制的套接字群組,並具有 作為 第一個成員。
  • 如果 g = 零,則不會執行任何群組作業。
對於未受限制的群組,只要單一服務提供者支援套接字,任何一組套接字都可以群組在一起。 受限制的套接字群組只能包含連線導向的套接字,而且要求所有群組套接字上的連線都位於相同主機上的相同位址。 針對新建立的套接字群組,可以使用 level 參數設定為 SOL_SOCKET ,並將 optname 參數設定為 SO_GROUP_ID 來擷取新的群組標識符。 套接字群組及其相關聯的套接字群組標識符會維持有效狀態,直到最後一個屬於此套接字群組的套接字關閉為止。 套接字群組標識碼在指定服務提供者的所有進程中都是唯一的。 套接字群組及其相關聯的標識符會維持有效狀態,直到屬於此套接字群組的最後一個套接字關閉為止。 套接字群組標識碼在指定服務提供者的所有進程中都是唯一的。 如需套接字群組的詳細資訊,請參閱 WSASocket 函式的。

傳遞至 condition 函式的 dwCallbackData 參數值,是在原始 WSAAccept 呼叫中當做 dwCallbackData 參數傳遞的值。 這個值只會由 Windows Socket 第 2 版用戶端解譯。 這可讓客戶端將 來自 WSAAccept 呼叫網站的一些內容資訊傳遞至條件函式。 這也會提供條件函式,並提供判斷是否接受連接所需的任何其他資訊。 典型的用法是傳遞 (適當地轉換) 指標至包含與此套接字相關聯之應用程式定義對象的參考的數據結構。

注意 若要保護 WSAAccept 函式的使用不受 SYN 攻擊,應用程式必須在報告連線要求之前,先執行完整的 TCP 交握 (SYN-SYNACK-ACK) 。 以這種方式防止 SYN 攻擊,會導致SO_CONDITIONAL_ACCEPT套接字選項變成內嵌;仍然會呼叫條件函式,而且 WSAAccept 函式會正常運作,但依賴客戶端無法執行交握的伺服器應用程式將無法正常運作。
 
注意 發出封鎖的 Winsock 呼叫時,例如 WSAAccept,Winsock 可能需要等候網路事件,才能完成呼叫。 在此情況下,Winsock 會執行可警示的等候,而異步過程調用 (APC) 排程在相同線程上可能會中斷。 在 APC 內發出另一個封鎖 Winsock 呼叫,中斷相同線程上持續封鎖 Winsock 呼叫會導致未定義的行為,而且永遠不會由 Winsock 客戶端嘗試。
 

範例程序代碼

下列範例示範 如何使用 WSAAccept 函 式。
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>

/* Define an example conditional function that depends on the pQos field */
int CALLBACK ConditionAcceptFunc(
    LPWSABUF lpCallerId,
    LPWSABUF lpCallerData,
    LPQOS pQos,
    LPQOS lpGQOS,
    LPWSABUF lpCalleeId,
    LPWSABUF lpCalleeData,
    GROUP FAR * g,
    DWORD_PTR dwCallbackData
    )
{

    if (pQos != NULL) {
        RtlZeroMemory(pQos, sizeof(QOS));
        return CF_ACCEPT;
    } else
        return CF_REJECT;
}

int main() {

    /* Declare and initialize variables */
    WSADATA wsaData;
    SOCKET ListenSocket, AcceptSocket;
    struct sockaddr_in saClient;
    int iClientSize = sizeof(saClient);
    u_short port = 27015;
    char* ip;
    sockaddr_in service;
    int error;

    /* Initialize Winsock */
    error = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (error) {
        printf("WSAStartup() failed with error: %d\n", error);
        return 1;
    }

    /* Create a TCP listening socket */
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {  
        printf("socket() failed with error: %d\n", WSAGetLastError() );
        WSACleanup();
        return 1;
    }

    /*-----------------------------------------  
     *  Set up the sock addr structure that the listening socket
     *  will be bound to. In this case, the structure holds the
     * local IP address and the port specified. */
    service.sin_family = AF_INET;
    service.sin_port = htons(port);
    hostent* thisHost;
    thisHost = gethostbyname("");
    ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);
    service.sin_addr.s_addr = inet_addr(ip);

    /*-----------------------------------------
     *  Bind the listening socket to the IP address.
     * and port number specified by the sockaddr structure. */
    error = bind(ListenSocket, (SOCKADDR *) &service, sizeof(SOCKADDR));
    if (error == SOCKET_ERROR) {  
        printf("bind() failed with error: %d\n", WSAGetLastError() );
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
  
    /* Make the socket listen for incoming connection requests */
    error = listen(ListenSocket, 1);
    if (error == SOCKET_ERROR) {  
        printf("listen() failed with error: %d\n", WSAGetLastError() );
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    printf("Listening...\n");
  
    /*-----------------------------------------
     *  Accept an incoming connection request on the
     *  listening socket and transfer control to the 
     * accepting socket. */
    AcceptSocket = WSAAccept(ListenSocket, (SOCKADDR*) &saClient, &iClientSize, 
        &ConditionAcceptFunc, NULL);
 
    /*  Now do some work with the AcceptSocket 
     *  At this point, the application could
     *  handle data transfer on the socket, or other socket
     * functionality.*/
    
    /* Then clean up and quit */

    closesocket(AcceptSocket);
    closesocket(ListenSocket);
    WSACleanup();

    return 0;
}

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

另請參閱

WSAAsyncSelect

WSAConnect

WSASocket

Winsock 函式

Winsock 參考

接受

bind

connect

getsockopt

listen

select

sockaddr

socket