AcceptEx 函数 (mswsock.h)

AcceptEx 函数接受新连接,返回本地和远程地址,并接收客户端应用程序发送的第一个数据块。

注意 此函数是 Windows 套接字规范中特定于 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

标识已使用 listen 函数调用的套接字的描述符。 服务器应用程序等待尝试在此套接字上进行连接。

[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 函数将执行三项任务:

  • 接受新连接。
  • 将返回连接的本地地址和远程地址。
  • 接收远程发送的第一个数据块。
注意必须在运行时通过调用 WSAIoctl 函数并指定SIO_GET_EXTENSION_FUNCTION_POINTER opcode 来获取 AcceptEx 函数的函数指针。 传递给 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 个字节,因为地址是以内部格式编写的。 例如,TCP/IP ) 的地址结构 (sockaddr_in的大小为 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
setsockopt (仅适用于 SO_UPDATE_ACCEPT_CONTEXT)
注意 如果使用 TF_DISCONNECT 和 TF_REUSE_SOCKET 标志调用 TransmitFile 函数,则指定的套接字已返回到既不绑定也不连接的状态。 然后,套接字句柄可以传递到 sAcceptSocket 参数中的 AcceptEx 函数,但套接字不能传递给 ConnectEx 函数。
 

当 AcceptEx 函数返回时,套接字 sAcceptSocket 处于连接套接字的默认状态。 在套接字上设置SO_UPDATE_ACCEPT_CONTEXT之前,套接字 sAcceptSocket 不会继承与 sListenSocket 参数关联的套接字的属性。 使用 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) );
   

如果提供了接收缓冲区,则在接受连接并读取数据之前,重叠操作不会完成。 使用具有 SO_CONNECT_TIME 选项的 getsockopt 函数检查是否已接受连接。 如果已接受,则可以确定建立连接的时间。 返回值是已连接套接字的秒数。 如果未连接套接字, 则 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 应用商店应用支持此函数。

Windows 8.1Windows Server 2012 R2:Windows 8.1、Windows Server 2012 R2 及更高版本的 Windows 应用商店应用支持此函数。

示例代码

以下示例使用具有重叠 I/O 和完成端口的 AcceptEx 函数。
#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 套接字 2 配合使用时,存在与连接设置相关的重要问题。 有关重要的 ATM 连接设置信息,请参阅 accept 函数文档中的“备注”部分。

要求

要求
最低受支持的客户端 Windows 8.1、Windows Vista [桌面应用 |UWP 应用]
最低受支持的服务器 Windows Server 2003 [桌面应用 | UWP 应用]
目标平台 Windows
标头 mswsock.h (包括 Mswsock.h)
Library Mswsock.lib
DLL Mswsock.dll

另请参阅

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

OVERLAPPED

TransmitFile

Winsock 函数

Winsock 参考

accept

closesocket

getsockopt

listen

sockaddr