Função AcceptEx (mswsock.h)

A função AcceptEx aceita uma nova conexão, retorna o endereço local e remoto e recebe o primeiro bloco de dados enviado pelo aplicativo cliente.

Nota Essa função é uma extensão específica da Microsoft para a especificação do Windows Sockets.

 

Sintaxe

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
);

Parâmetros

[in] sListenSocket

Um descritor que identifica um soquete que já foi chamado com a função de escuta . Um aplicativo de servidor aguarda as tentativas de conexão nesse soquete.

[in] sAcceptSocket

Um descritor que identifica um soquete no qual aceitar uma conexão de entrada. Esse soquete não deve ser associado ou conectado.

[in] lpOutputBuffer

Um ponteiro para um buffer que recebe o primeiro bloco de dados enviado em uma nova conexão, o endereço local do servidor e o endereço remoto do cliente. Os dados de recebimento são gravados na primeira parte do buffer começando no deslocamento zero, enquanto os endereços são gravados na última parte do buffer. Esse parâmetro precisa ser especificado.

[in] dwReceiveDataLength

O número de bytes em lpOutputBuffer que serão usados para dados de recebimento reais no início do buffer. Esse tamanho não deve incluir o tamanho do endereço local do servidor, nem o endereço remoto do cliente; eles são acrescentados ao buffer de saída. Se dwReceiveDataLength for zero, aceitar a conexão não resultará em uma operação de recebimento. Em vez disso, AcceptEx é concluído assim que uma conexão chega, sem aguardar nenhum dado.

[in] dwLocalAddressLength

O número de bytes reservados para as informações de endereço local. Esse valor deve ser pelo menos 16 bytes a mais do que o comprimento máximo do endereço para o protocolo de transporte em uso.

[in] dwRemoteAddressLength

O número de bytes reservados para as informações de endereço remoto. Esse valor deve ser pelo menos 16 bytes a mais do que o comprimento máximo do endereço para o protocolo de transporte em uso. Não pode ser zero.

[out] lpdwBytesReceived

Um ponteiro para um DWORD que recebe a contagem de bytes recebidos. Esse parâmetro será definido somente se a operação for concluída de forma síncrona. Se ele retornar ERROR_IO_PENDING e for concluído posteriormente, esse DWORD nunca será definido e você deverá obter o número de bytes lidos do mecanismo de notificação de conclusão.

[in] lpOverlapped

Uma estrutura OVERLAPPED usada para processar a solicitação. Esse parâmetro deve ser especificado; não pode ser NULL.

Retornar valor

Se nenhum erro ocorrer, a função AcceptEx será concluída com êxito e um valor true será retornado.

Se a função falhar, AcceptEx retornará FALSE. A função WSAGetLastError pode ser chamada para retornar informações de erro estendidas. Se WSAGetLastError retornar ERROR_IO_PENDING, a operação foi iniciada com êxito e ainda está em andamento. Se o erro for WSAECONNRESET, uma conexão de entrada foi indicada, mas foi posteriormente encerrada pelo par remoto antes de aceitar a chamada.

Comentários

A função AcceptEx combina várias funções de soquete em uma única transição de API/kernel. A função AcceptEx , quando bem-sucedida, executa três tarefas:

  • Uma nova conexão é aceita.
  • Os endereços locais e remotos da conexão são retornados.
  • O primeiro bloco de dados enviados pelo remoto é recebido.
Nota O ponteiro de função para a função AcceptEx deve ser obtido em tempo de execução fazendo uma chamada para a função WSAIoctl com o SIO_GET_EXTENSION_FUNCTION_POINTER opcode especificado. O buffer de entrada passado para a função WSAIoctl deve conter WSAID_ACCEPTEX, um GUID (identificador global exclusivo) cujo valor identifica a função de extensão AcceptEx . Em caso de êxito, a saída retornada pela função WSAIoctl contém um ponteiro para a função AcceptEx . O GUID WSAID_ACCEPTEX é definido no arquivo de cabeçalho Mswsock.h .
 

Um programa pode fazer uma conexão com um soquete mais rapidamente usando AcceptEx em vez da função accept .

Um único buffer de saída recebe os dados, o endereço do soquete local (o servidor) e o endereço do soquete remoto (o cliente).

O uso de um único buffer melhora o desempenho. Ao usar AcceptEx, a função GetAcceptExSockaddrs deve ser chamada para analisar o buffer em suas três partes distintas (dados, endereço de soquete local e endereço de soquete remoto). No Windows XP e posterior, depois que a função AcceptEx for concluída e a opção SO_UPDATE_ACCEPT_CONTEXT for definida no soquete aceito, o endereço local associado ao soquete aceito também poderá ser recuperado usando a função getsockname . Da mesma forma, o endereço remoto associado ao soquete aceito pode ser recuperado usando a função getpeername .

O tamanho do buffer para o endereço local e remoto deve ser 16 bytes a mais do que o tamanho da estrutura sockaddr para o protocolo de transporte em uso porque os endereços são gravados em um formato interno. Por exemplo, o tamanho de um sockaddr_in (a estrutura de endereços para TCP/IP) é de 16 bytes. Portanto, um tamanho de buffer de pelo menos 32 bytes deve ser especificado para os endereços locais e remotos.

A função AcceptEx usa E/S sobreposta, ao contrário da função accept . Se o aplicativo usar AcceptEx, ele poderá atender a um grande número de clientes com um número relativamente pequeno de threads. Assim como acontece com todas as funções sobrepostas do Windows, os eventos do Windows ou as portas de conclusão podem ser usados como um mecanismo de notificação de conclusão.

Outra diferença importante entre a função AcceptEx e a função accept é que AcceptEx exige que o chamador já tenha dois soquetes:

  • Um que especifica o soquete no qual escutar.
  • Um que especifica o soquete no qual aceitar a conexão.

O parâmetro sAcceptSocket deve ser um soquete aberto que não esteja associado nem conectado.

O parâmetro lpNumberOfBytesTransferred da função GetQueuedCompletionStatus ou da função GetOverlappedResult indica o número de bytes recebidos na solicitação.

Quando essa operação for concluída com êxito, sAcceptSocket poderá ser passado, mas apenas para as seguintes funções:

ReadFile
WriteFile
send
WSASend
Recv
WSARecv
Transmitfile
Closesocket
setsockopt(only for SO_UPDATE_ACCEPT_CONTEXT)
Nota Se a função TransmitFile for chamada com os sinalizadores TF_DISCONNECT e TF_REUSE_SOCKET, o soquete especificado será retornado a um estado no qual não está associado nem conectado. O identificador de soquete pode ser passado para a função AcceptEx no parâmetro sAcceptSocket , mas o soquete não pode ser passado para a função ConnectEx .
 

Quando a função AcceptEx retorna, o soquete sAcceptSocket está no estado padrão de um soquete conectado. O soquete sAcceptSocket não herda as propriedades do soquete associado ao parâmetro sListenSocket até que SO_UPDATE_ACCEPT_CONTEXT seja definido no soquete. Use a função setsockopt para definir a opção SO_UPDATE_ACCEPT_CONTEXT, especificando sAcceptSocket como o identificador de soquete e sListenSocket como o valor da opção.

Por exemplo:

//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) );
   

Se um buffer de recebimento for fornecido, a operação sobreposta não será concluída até que uma conexão seja aceita e os dados sejam lidos. Use a função getsockopt com a opção SO_CONNECT_TIME para marcar se uma conexão foi aceita. Se ela tiver sido aceita, você poderá determinar por quanto tempo a conexão foi estabelecida. O valor retornado é o número de segundos que o soquete foi conectado. Se o soquete não estiver conectado, o getsockopt retornará 0xFFFFFFFF. Os aplicativos que marcar se a operação sobreposta foi concluída, em combinação com a opção SO_CONNECT_TIME, podem determinar se uma conexão foi aceita, mas nenhum dado foi recebido. Examinar uma conexão dessa maneira permite que um aplicativo determine se as conexões estabelecidas por um tempo não receberam dados. É recomendável que essas conexões sejam encerradas fechando o soquete aceito, o que força a chamada da função AcceptEx a ser concluída com um erro.

Por exemplo:


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);
}

Nota Todas as E/S iniciadas por um determinado thread são canceladas quando esse thread é encerrado. Para soquetes sobrepostos, as operações assíncronas pendentes podem falhar se o thread for fechado antes da conclusão das operações. Consulte ExitThread para obter mais informações.
 

Windows Phone 8: essa função tem suporte para aplicativos da Windows Phone Store no Windows Phone 8 e posterior.

Windows 8.1 e Windows Server 2012 R2: essa função tem suporte para aplicativos da Windows Store em Windows 8.1, Windows Server 2012 R2 e posteriores.

Código de exemplo

O exemplo a seguir usa a função AcceptEx usando E/S sobrepostas e portas de conclusã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;
}


Notas para QoS

A função TransmitFile permite a configuração de dois sinalizadores, TF_DISCONNECT ou TF_REUSE_SOCKET, que retornam o soquete a um estado "desconectado e reutilizável" após a transmissão do arquivo. Esses sinalizadores não devem ser usados em um soquete em que a qualidade do serviço foi solicitada, pois o provedor de serviços pode excluir imediatamente qualquer qualidade de serviço associada ao soquete antes que a transferência de arquivo seja concluída. A melhor abordagem para um soquete habilitado para QoS é simplesmente chamar a função closesocket quando a transferência de arquivo for concluída, em vez de depender desses sinalizadores.

Anotações para caixa eletrônico

Há problemas importantes associados à configuração da conexão ao usar o ATM (Modo de Transferência Assíncrona) com o Windows Sockets 2. Consulte a seção Comentários na documentação da função accept para obter informações importantes de configuração de conexão de caixa eletrônico.

Requisitos

Requisito Valor
Cliente mínimo com suporte Windows 8.1, Windows Vista [aplicativos da área de trabalho | Aplicativos UWP]
Servidor mínimo com suporte Windows Server 2003 [aplicativos da área de trabalho | Aplicativos UWP]
Plataforma de Destino Windows
Cabeçalho mswsock.h (inclua Mswsock.h)
Biblioteca Mswsock.lib
DLL Mswsock.dll

Confira também

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

OVERLAPPED

Transmitfile

Funções Winsock

Referência de Winsock

accept

Closesocket

Getsockopt

listen

Sockaddr