AcceptEx 函式 (mswsock.h)

AcceptEx 函式會接受新的連線、傳回本機和遠端位址,並接收用戶端應用程式所傳送的第一個數據區塊。

注意 此函式是 Windows Sockets 規格的 Microsoft 特定擴充功能。

 

語法

BOOL AcceptEx(
  [in]  SOCKET       sListenSocket,
  [in]  SOCKET       sAcceptSocket,
  [in]  PVOID        lpOutputBuffer,
  [in]  DWORD        dwReceiveDataLength,
  [in]  DWORD        dwLocalAddressLength,
  [in]  DWORD        dwRemoteAddressLength,
  [out] LPDWORD      lpdwBytesReceived,
  [in]  LPOVERLAPPED lpOverlapped
);

參數

[in] sListenSocket

描述項,識別已使用 接聽 函式呼叫的套接字。 伺服器應用程式會等候嘗試在此套接字上連線。

[in] sAcceptSocket

描述項,識別要接受連入連線的套接字。 此套接字不得系結或連線。

[in] lpOutputBuffer

緩衝區的指標,接收在新連接上傳送的第一個數據區塊、伺服器的本機位址,以及客戶端的遠端位址。 接收數據會從位移零開始寫入緩衝區的第一個部分,而位址會寫入緩衝區的後半部。 必須指定此參數。

[in] dwReceiveDataLength

lpOutputBuffer 中將用於緩衝區開頭實際接收數據的位元元組數目。 此大小不應包含伺服器的本機位址大小,也不應包含客戶端的遠端位址;它們會附加至輸出緩衝區。 如果 dwReceiveDataLength 為零,接受連接將不會產生接收作業。 相反地, AcceptEx 會在連線送達時立即完成,而不需要等待任何數據。

[in] dwLocalAddressLength

保留給本機地址資訊的位元組數目。 此值至少必須大於使用中傳輸通訊協議的位址長度上限 16 個字節。

[in] dwRemoteAddressLength

保留給遠端地址資訊的位元組數目。 此值至少必須大於使用中傳輸通訊協議的位址長度上限 16 個字節。 不能為零。

[out] lpdwBytesReceived

接收所接收位元組計數的 DWORD 指標。 只有在作業同步完成時,才會設定此參數。 如果傳回ERROR_IO_PENDING且稍後完成,則永遠不會設定此 DWORD ,而且您必須取得從完成通知機制讀取的位元元數目。

[in] lpOverlapped

用來處理要求的 OVERLAPPED 結構。 必須指定此參數;它不能是 NULL

傳回值

如果沒有發生錯誤, AcceptEx 函式會順利完成,並傳回 TRUE 值。

如果函式失敗, AcceptEx 會 傳回 FALSE。 然後可以呼叫 WSAGetLastError 函 式以傳回擴充的錯誤資訊。 如果 WSAGetLastError 傳回 ERROR_IO_PENDING,則作業已成功起始且仍在進行中。 如果錯誤為 WSAECONNRESET,表示傳入連線,但後續由遠端對等終止,再接受呼叫。

備註

AcceptEx 函式會將數個套接字函式結合成單一 API/核心轉換。 AcceptEx 函式會在成功時執行三項工作:

  • 接受新的連線。
  • 會傳回連線的本機和遠端位址。
  • 收到遠端傳送的第一個數據區塊。
注意 若要在運行時間取得 AcceptEx 函式的函式指標,您必須呼叫 WSAIoctl 函式並指定 SIO_GET_EXTENSION_FUNCTION_POINTER opcode。 傳遞至 WSAIoctl 函式的輸入緩衝區必須包含 WSAID_ACCEPTEX,這是全域唯一標識碼 (GUID) 其值可識別 AcceptEx 擴充函式。 成功時, WSAIoctl 函式所傳回的輸出會包含 AcceptEx 函 式的指標。 WSAID_ACCEPTEX GUID 定義於 Mswsock.h 頭檔中。
 

程式可以使用 AcceptEx 來更快速地連線到套接字,而不是 accept 函式。

單一輸出緩衝區會接收數據、伺服器) (本機套接字位址,以及用戶端) (遠端套接字位址。

使用單一緩衝區可改善效能。 使用 AcceptEx 時,必須呼叫 GetAcceptExSockaddrs 函式,將緩衝區剖析成其三個不同的部分, (數據、本機套接字位址和遠端套接字位址) 。 在 Windows XP 和更新版本上,一旦 AcceptEx 函式完成,而且在接受的套接字上設定 了 SO_UPDATE_ACCEPT_CONTEXT 選項,也可以使用 getsockname 函式來擷取與接受套接字相關聯的本機位址。 同樣地,可以使用 getpeername 函式來擷取與接受套接字相關聯的遠端位址。

本機和遠端位址的緩衝區大小必須大於使用中傳輸通訊協定 的 sockaddr 結構大小 16 個字節,因為位址是以內部格式寫入。 例如, sockaddr_in 的大小 (TCP/IP) 的地址結構為 16 個字節。 因此,必須針對本機和遠端位址指定至少 32 個字節的緩衝區大小。

AcceptEx 函式使用重疊的 I/O,與 accept 函式不同。 如果您的應用程式使用 AcceptEx,它可以服務大量具有相對少量線程的用戶端。 如同所有重疊的 Windows 函式,Windows 事件或完成埠都可以當做完成通知機制使用。

AcceptEx 函式與 accept 函式之間的另一個主要差異是 AcceptEx 要求呼叫端已經有兩個套接字:

  • 指定要接聽之套接字的 。
  • 指定要接受連線之套接字的 。

sAcceptSocket 參數必須是未系結或連線的開放套接字。

GetQueuedCompletionStatus 函式或 GetOverlappedResult 函式的 lpNumberOfBytesTransferred 參數表示要求中收到的位元組數目。

成功完成此作業時,可以傳遞 sAcceptSocket ,但只能傳遞至下列函式:

ReadFile
WriteFile
send
WSASend
recv
WSARecv
TransmitFile
closesocket
setockopt (僅適用於 SO_UPDATE_ACCEPT_CONTEXT)
注意 如果使用 TF_DISCONNECT 和 TF_REUSE_SOCKET 旗標呼叫 TransmitFile 函式,則指定的套接字已傳回至其未系結或連線的狀態。 套接字句柄接著可以傳遞至 sAcceptSocket 參數中的 AcceptEx 函式,但套接字無法傳遞至 ConnectEx 函式。
 

AcceptEx 函式傳回時,套接字 sAcceptSocket 處於連線套接字的默認狀態。 套接字 sAcceptSocket 不會繼承與 sListenSocket 參數相關聯的套接字屬性,直到套接字上設定SO_UPDATE_ACCEPT_CONTEXT為止。 使用 setsockopt 函式來設定SO_UPDATE_ACCEPT_CONTEXT選項,將 sAcceptSocket 指定為套接字句柄,並將 sListenSocket 指定為選項值。

例如:

//Need to #include <mswsock.h> for SO_UPDATE_ACCEPT_CONTEXT

int iResult = 0;

iResult =  setsockopt( sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, 
    (char *)&sListenSocket, sizeof(sListenSocket) );
   

如果提供接收緩衝區,則重疊的作業將不會完成,直到接受連接並讀取數據為止。 使用 getsockopt 函式搭配 SO_CONNECT_TIME 選項來檢查是否已接受連線。 如果已接受,您可以判斷已建立連線的時間長度。 傳回值是套接字已連接的秒數。 如果未連接套接字, 則 getsockopt 會傳回0xFFFFFFFF。 檢查重迭作業是否已完成的應用程式,結合 [SO_CONNECT_TIME] 選項,可以判斷已接受連線,但未收到任何數據。 以這種方式檢查連線,可讓應用程式判斷是否已建立一段時間的連線未收到任何數據。 建議您藉由關閉接受的套接字來終止這類連線,這會強制 AcceptEx 函數調用完成併發生錯誤。

例如:


INT seconds;
INT bytes = sizeof(seconds);
int iResult = 0;

iResult = getsockopt( sAcceptSocket, SOL_SOCKET, SO_CONNECT_TIME,
                      (char *)&seconds, (PINT)&bytes );

if ( iResult != NO_ERROR ) {
    printf( "getsockopt(SO_CONNECT_TIME) failed: %u\n", WSAGetLastError( ) );
    exit(1);
}

注意 當該線程結束時,指定的線程所起始的所有 I/O 都會取消。 對於重疊的套接字,如果線程在作業完成之前關閉,擱置的異步操作可能會失敗。 如需詳細資訊,請參閱 ExitThread
 

Windows Phone 8:Windows Phone 8 和更新版本上的 Windows Phone Store 應用程式支援此函式。

Windows 8.1Windows Server 2012 R2:Windows 8.1、Windows Server 2012 R2 及更新版本上的 Windows 市集應用程式支援此函式。

範例程序代碼

下列範例使用 AcceptEx 函式,使用重疊的 I/O 和完成埠。
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

int main()
{
    //----------------------------------------
    // Declare and initialize variables
    WSADATA wsaData;
    int iResult = 0;
    BOOL bRetVal = FALSE;

    HANDLE hCompPort;
    HANDLE hCompPort2;
    
    LPFN_ACCEPTEX lpfnAcceptEx = NULL;
    GUID GuidAcceptEx = WSAID_ACCEPTEX;
    WSAOVERLAPPED olOverlap;

    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET AcceptSocket = INVALID_SOCKET;
    sockaddr_in service;
    char lpOutputBuf[1024];
    int outBufLen = 1024;
    DWORD dwBytes;

    hostent *thisHost;
    char *ip;
    u_short port;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"Error at WSAStartup\n");
        return 1;
    }    

    // Create a handle for the completion port
    hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long) 0, 0);
    if (hCompPort == NULL) {
        wprintf(L"CreateIoCompletionPort failed with error: %u\n",
            GetLastError() );
        WSACleanup();
        return 1;
    }
            
    // Create a listening socket
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        wprintf(L"Create of ListenSocket socket failed with error: %u\n",
            WSAGetLastError() );
        WSACleanup();
        return 1;
    }

    // Associate the listening socket with the completion port
    CreateIoCompletionPort((HANDLE) ListenSocket, hCompPort, (u_long) 0, 0);

    //----------------------------------------
    // Bind the listening socket to the local IP address
    // and port 27015
    port = 27015;
    thisHost = gethostbyname("");
    ip = inet_ntoa(*(struct in_addr *) *thisHost->h_addr_list);

    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr(ip);
    service.sin_port = htons(port);

    if (bind(ListenSocket, (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) {
        wprintf(L"bind failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    //----------------------------------------
    // Start listening on the listening socket
    iResult = listen(ListenSocket, 100);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"listen failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    wprintf(L"Listening on address: %s:%d\n", ip, port);

    // Load the AcceptEx function into memory using WSAIoctl.
    // The WSAIoctl function is an extension of the ioctlsocket()
    // function that can use overlapped I/O. The function's 3rd
    // through 6th parameters are input and output buffers where
    // we pass the pointer to our AcceptEx function. This is used
    // so that we can call the AcceptEx function directly, rather
    // than refer to the Mswsock.lib library.
    iResult = WSAIoctl(ListenSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
             &GuidAcceptEx, sizeof (GuidAcceptEx), 
             &lpfnAcceptEx, sizeof (lpfnAcceptEx), 
             &dwBytes, NULL, NULL);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"WSAIoctl failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Create an accepting socket
    AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (AcceptSocket == INVALID_SOCKET) {
        wprintf(L"Create accept socket failed with error: %u\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Empty our overlapped structure and accept connections.
    memset(&olOverlap, 0, sizeof (olOverlap));

    bRetVal = lpfnAcceptEx(ListenSocket, AcceptSocket, lpOutputBuf,
                 outBufLen - ((sizeof (sockaddr_in) + 16) * 2),
                 sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, 
                 &dwBytes, &olOverlap);
    if (bRetVal == FALSE) {
        wprintf(L"AcceptEx failed with error: %u\n", WSAGetLastError());
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // Associate the accept socket with the completion port
    hCompPort2 = CreateIoCompletionPort((HANDLE) AcceptSocket, hCompPort, (u_long) 0, 0); 
    // hCompPort2 should be hCompPort if this succeeds
    if (hCompPort2 == NULL) {
        wprintf(L"CreateIoCompletionPort associate failed with error: %u\n",
            GetLastError() );
        closesocket(AcceptSocket);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    
    // Continue on to use send, recv, TransmitFile(), etc.,.
    //...

    return 0;
}


QoS 的注意事項

TransmitFile 函式允許在傳輸檔案之後,將套接字傳回「已中斷連線、可重複使用」狀態的兩個旗標TF_DISCONNECT或TF_REUSE_SOCKET。 這些旗標不應該用於要求服務品質的套接字上,因為服務提供者可能會在文件傳輸完成之前立即刪除與套接字相關聯的任何服務品質。 啟用 QoS 的套接字最佳方法是在文件傳輸完成時呼叫 closesocket 函式,而不是依賴這些旗標。

ATM 的注意事項

使用異步傳輸模式 (ATM) Windows Sockets 2 時,連線設定有一個重要問題。 如需重要 ATM 連線設定資訊,請參閱 接受 函式檔中的一節。

規格需求

需求
最低支援的用戶端 Windows 8.1、Windows Vista [傳統型應用程式 |UWP 應用程式]
最低支援的伺服器 Windows Server 2003 [傳統型應用程式 |UWP 應用程式]
目標平台 Windows
標頭 mswsock.h (包含 Mswsock.h)
程式庫 Mswsock.lib
Dll Mswsock.dll

另請參閱

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

重疊

TransmitFile

Winsock 函式

Winsock 參考

接受

closesocket

getsockopt

listen

sockaddr