Función AcceptEx (winsock.h)

La función AcceptEx acepta una nueva conexión, devuelve la dirección local y remota y recibe el primer bloque de datos enviados por la aplicación cliente.

Nota Esta función es una extensión específica de Microsoft para la especificación de Windows Sockets.

 

Sintaxis

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

Descriptor que identifica un socket al que ya se ha llamado con la función de escucha . Una aplicación de servidor espera los intentos de conexión en este socket.

[in] sAcceptSocket

Descriptor que identifica un socket en el que se va a aceptar una conexión entrante. Este socket no debe estar enlazado ni conectado.

[in] lpOutputBuffer

Puntero a un búfer que recibe el primer bloque de datos enviados en una nueva conexión, la dirección local del servidor y la dirección remota del cliente. Los datos de recepción se escriben en la primera parte del búfer a partir de cero de desplazamiento, mientras que las direcciones se escriben en la última parte del búfer. Este parámetro debe especificarse.

[in] dwReceiveDataLength

Número de bytes en lpOutputBuffer que se usará para los datos de recepción reales al principio del búfer. Este tamaño no debe incluir el tamaño de la dirección local del servidor ni la dirección remota del cliente; se anexan al búfer de salida. Si dwReceiveDataLength es cero, aceptar la conexión no dará lugar a una operación de recepción. En su lugar, AcceptEx se completa en cuanto llega una conexión, sin esperar a ningún dato.

[in] dwLocalAddressLength

Número de bytes reservados para la información de dirección local. Este valor debe tener al menos 16 bytes más que la longitud máxima de la dirección para el protocolo de transporte en uso.

[in] dwRemoteAddressLength

Número de bytes reservados para la información de la dirección remota. Este valor debe tener al menos 16 bytes más que la longitud máxima de la dirección para el protocolo de transporte en uso. No puede ser cero.

[out] lpdwBytesReceived

Puntero a un DWORD que recibe el recuento de bytes recibidos. Este parámetro solo se establece si la operación se completa de forma sincrónica. Si devuelve ERROR_IO_PENDING y se completa más adelante, este DWORD nunca se establece y debe obtener el número de bytes leídos desde el mecanismo de notificación de finalización.

[in] lpOverlapped

Estructura SUPERPUESTA que se usa para procesar la solicitud. Este parámetro debe especificarse; no puede ser NULL.

Valor devuelto

Si no se produce ningún error, la función AcceptEx se completó correctamente y se devuelve un valor de TRUE .

Si se produce un error en la función, AcceptEx devuelve FALSE. A continuación, se puede llamar a la función WSAGetLastError para devolver información de error extendida. Si WSAGetLastError devuelve ERROR_IO_PENDING, la operación se inició correctamente y todavía está en curso. Si el error es WSAECONNRESET, se indicó una conexión entrante, pero posteriormente el par remoto finalizó antes de aceptar la llamada.

Comentarios

La función AcceptEx combina varias funciones de socket en una única transición de API o kernel. La función AcceptEx , cuando se ejecuta correctamente, realiza tres tareas:

  • Se acepta una nueva conexión.
  • Se devuelven las direcciones locales y remotas de la conexión.
  • Se recibe el primer bloque de datos enviados por el remoto.
Nota El puntero de función para la función AcceptEx debe obtenerse en tiempo de ejecución realizando una llamada a la función WSAIoctl con el código de operación SIO_GET_EXTENSION_FUNCTION_POINTER especificado. El búfer de entrada pasado a la función WSAIoctl debe contener WSAID_ACCEPTEX, un identificador único global (GUID) cuyo valor identifica la función de extensión AcceptEx . Si se ejecuta correctamente, la salida devuela por la función WSAIoctl contiene un puntero a la función AcceptEx . El GUID de WSAID_ACCEPTEX se define en el archivo de encabezado Mswsock.h .
 

Un programa puede hacer que una conexión a un socket sea más rápido mediante AcceptEx en lugar de la función accept .

Un único búfer de salida recibe los datos, la dirección del socket local (el servidor) y la dirección del socket remoto (el cliente).

El uso de un solo búfer mejora el rendimiento. Cuando se usa AcceptEx, se debe llamar a la función GetAcceptExSockaddrs para analizar el búfer en sus tres partes distintas (datos, dirección de socket local y dirección de socket remoto). En Windows XP y versiones posteriores, una vez completada la función AcceptEx y la opción SO_UPDATE_ACCEPT_CONTEXT se establece en el socket aceptado, la dirección local asociada al socket aceptado también se puede recuperar mediante la función getsockname . Del mismo modo, la dirección remota asociada al socket aceptado se puede recuperar mediante la función getpeername .

El tamaño del búfer para la dirección local y remota debe ser de 16 bytes más que el tamaño de la estructura sockaddr para el protocolo de transporte en uso porque las direcciones se escriben en un formato interno. Por ejemplo, el tamaño de un sockaddr_in (la estructura de direcciones para TCP/IP) es de 16 bytes. Por lo tanto, se debe especificar un tamaño de búfer de al menos 32 bytes para las direcciones locales y remotas.

La función AcceptEx usa E/S superpuesta, a diferencia de la función accept . Si la aplicación usa AcceptEx, puede atender un gran número de clientes con un número relativamente pequeño de subprocesos. Al igual que con todas las funciones superpuestas de Windows, los eventos de Windows o los puertos de finalización se pueden usar como mecanismo de notificación de finalización.

Otra diferencia clave entre la función AcceptEx y la función accept es que AcceptEx requiere que el autor de la llamada ya tenga dos sockets:

  • Uno que especifica el socket en el que se va a escuchar.
  • Uno que especifica el socket en el que se va a aceptar la conexión.

El parámetro sAcceptSocket debe ser un socket abierto que no esté enlazado ni conectado.

El parámetro lpNumberOfBytesTransferred de la función GetQueuedCompletionStatus o la función GetOverlappedResult indica el número de bytes recibidos en la solicitud.

Cuando esta operación se completa correctamente, se puede pasar sAcceptSocket , pero solo a las funciones siguientes:

ReadFile
WriteFile
enviar
WSASend
recv
WSARecv
TransmitFile
closesocket
setsockopt(solo para SO_UPDATE_ACCEPT_CONTEXT)
Nota Si se llama a la función TransmitFile con las marcas TF_DISCONNECT y TF_REUSE_SOCKET, el socket especificado se devuelve a un estado en el que no está enlazado ni conectado. A continuación, el identificador de socket se puede pasar a la función AcceptEx en el parámetro sAcceptSocket , pero el socket no se puede pasar a la función ConnectEx .
 

Cuando se devuelve la función AcceptEx , el socket sAcceptSocket está en el estado predeterminado de un socket conectado. El socket sAcceptSocket no hereda las propiedades del socket asociado al parámetro sListenSocket hasta que SO_UPDATE_ACCEPT_CONTEXT se establece en el socket. Use la función setsockopt para establecer la opción SO_UPDATE_ACCEPT_CONTEXT, especificando sAcceptSocket como identificador de socket y sListenSocket como valor de opción.

Por ejemplo:

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

Si se proporciona un búfer de recepción, la operación superpuesta no se completará hasta que se acepte una conexión y se lean los datos. Use la función getockopt con la opción SO_CONNECT_TIME para comprobar si se ha aceptado una conexión. Si se ha aceptado, puede determinar cuánto tiempo se ha establecido la conexión. El valor devuelto es el número de segundos que el socket se ha conectado. Si el socket no está conectado, getsockopt devuelve 0xFFFFFFFF. Las aplicaciones que comprueban si la operación superpuesta se ha completado, en combinación con la opción SO_CONNECT_TIME, pueden determinar que se ha aceptado una conexión, pero no se han recibido datos. Examinar una conexión de esta manera permite a una aplicación determinar si las conexiones que se han establecido durante un tiempo no han recibido datos. Se recomienda que dichas conexiones finalicen cerrando el socket aceptado, lo que obliga a que la llamada de función AcceptEx se complete con un error.

Por ejemplo:


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 las E/S iniciadas por un subproceso determinado se cancelan cuando se cierra ese subproceso. En el caso de los sockets superpuestos, las operaciones asincrónicas pendientes pueden producir un error si el subproceso se cierra antes de que se completen las operaciones. Consulte ExitThread para obtener más información.
 

Windows Phone 8: esta función es compatible con las aplicaciones de Windows Phone Store en Windows Phone 8 y versiones posteriores.

Windows 8.1 y Windows Server 2012 R2: esta función es compatible con las aplicaciones de la Tienda Windows en Windows 8.1, Windows Server 2012 R2 y versiones posteriores.

Código de ejemplo

En el ejemplo siguiente se usa la función AcceptEx mediante puertos de E/S y finalización superpuestos.
#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 de QoS

La función TransmitFile permite establecer dos marcas, TF_DISCONNECT o TF_REUSE_SOCKET, que devuelven el socket a un estado "desconectado y reutilizable" después de que se haya transmitido el archivo. Estas marcas no deben utilizarse en un socket en el que se haya solicitado la calidad del servicio, ya que el proveedor de servicios puede eliminar inmediatamente cualquier calidad de servicio asociada al socket antes de que se haya completado la transferencia de archivos. El mejor enfoque para un socket habilitado para QoS es simplemente llamar a la función closesocket cuando se haya completado la transferencia de archivos, en lugar de confiar en estas marcas.

Notas para ATM

Hay problemas importantes asociados con la configuración de conexión al usar el modo de transferencia asincrónica (ATM) con Windows Sockets 2. Consulte la sección Comentarios en la documentación de la función accept para obtener información importante sobre la configuración de la conexión ATM.

Requisitos

Requisito Value
Cliente mínimo compatible Windows 8.1, Windows Vista [aplicaciones de escritorio | Aplicaciones para UWP]
Servidor mínimo compatible Windows Server 2003 [aplicaciones de escritorio | aplicaciones para UWP]
Plataforma de destino Windows
Encabezado winsock.h (incluya Mswsock.h)
Library Mswsock.lib
Archivo DLL Mswsock.dll

Consulte también

GetAcceptExSockaddrs

GetOverlappedResult

GetQueuedCompletionStatus

OVERLAPPED

TransmitFile

Funciones winsock

Referencia de Winsock

accept

closesocket

getsockopt

listen

sockaddr