Compartir a través de


Escucha y aceptación de conexiones entrantes

Después de que una aplicación Winsock Kernel (WSK) enlace un socket de escucha a una dirección de transporte local, el socket comienza a escuchar las conexiones entrantes desde direcciones de transporte remoto. Una aplicación WSK puede aceptar una conexión entrante en un socket de escucha llamando a la función WskAccept . El IRP que la aplicación pasa a la función WskAccept se pone en cola hasta que llega una conexión entrante.

En el ejemplo de código siguiente se muestra cómo una aplicación WSK puede aceptar una conexión entrante llamando a la función WskAccept .

// Prototype for the accept IoCompletion routine
NTSTATUS
  AcceptComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    );

// Function to accept an incoming connection
NTSTATUS
  AcceptConnection(
    PWSK_SOCKET Socket,
    PVOID AcceptSocketContext,
    PWSK_CLIENT_CONNECTION_DISPATCH AcceptSocketDispatch
    )
{
  PWSK_PROVIDER_LISTEN_DISPATCH Dispatch;
  PIRP Irp;
  NTSTATUS Status;

  // Get pointer to the socket's provider dispatch structure
  Dispatch =
    (PWSK_PROVIDER_LISTEN_DISPATCH)(Socket->Dispatch);

  // Allocate an IRP
  Irp =
    IoAllocateIrp(
      1,
      FALSE
      );

  // Check result
  if (!Irp)
  {
    // Return error
    return STATUS_INSUFFICIENT_RESOURCES;
  }

  // Set the completion routine for the IRP
  IoSetCompletionRoutine(
    Irp,
    AcceptComplete,
    AcceptSocketContext,
    TRUE,
    TRUE,
    TRUE
    );

  // Initiate the accept operation on the socket
  Status =
    Dispatch->WskAccept(
      Socket,
      0,  // No flags
      AcceptSocketContext,
      AcceptSocketDispatch,
      NULL,
      NULL,
      Irp
      );

  // Return the status of the call to WskAccept()
  return Status;
}

// The accept IoCompletion routine
NTSTATUS
  AcceptComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
  UNREFERENCED_PARAMETER(DeviceObject);

  PWSK_SOCKET Socket;
  PVOID AcceptSocketContext;

  // Check the result of the accept operation
  if (Irp->IoStatus.Status == STATUS_SUCCESS)
  {
    // Get the accepted socket object from the IRP
    Socket = (PWSK_SOCKET)(Irp->IoStatus.Information);

    // Get the accepted socket's context
    AcceptSocketContext = Context;

    // Perform the next operation on the accepted socket
    ...
  }

  // Error status
  else
  {
    // Handle error
    ...
  }

  // Free the IRP
  IoFreeIrp(Irp);

  // Always return STATUS_MORE_PROCESSING_REQUIRED to
  // terminate the completion processing of the IRP.
  return STATUS_MORE_PROCESSING_REQUIRED;
}

Como alternativa a llamar a la función WskAccept para aceptar conexiones entrantes en un socket de escucha, una aplicación WSK puede habilitar la función de devolución de llamada de eventos WskAcceptEvent en el socket. Si una aplicación WSK habilita la función de devolución de llamada de eventos WskAcceptEvent en un socket de escucha, el subsistema WSK llama a la función de devolución de llamada de eventos WskAcceptEvent del socket cada vez que se acepta una nueva conexión entrante en el socket. Para obtener más información sobre cómo habilitar la función de devolución de llamada de eventos WskAcceptEvent de un socket de escucha, consulte Habilitación y deshabilitación de funciones de devolución de llamada de eventos.

En el ejemplo de código siguiente se muestra cómo una aplicación WSK puede aceptar una conexión entrante mediante el subsistema WSK que llama a la función de devolución de llamada de eventos WskAcceptEvent de un socket de escucha.

// Dispatch table of event callback functions for accepted sockets
const WSK_CLIENT_CONNECTION_DISPATCH ConnectionDispatch =
{
  .
  . // Function pointers for the event callback functions
  .
};

// Pool tag used for allocating the socket context
#define SOCKET_CONTEXT_POOL_TAG 'tpcs'

// A listening socket's WskAcceptEvent event callback function
NTSTATUS WSKAPI
  WskAcceptEvent(
    PVOID SocketContext,
    ULONG Flags,
    PSOCKADDR LocalAddress,
    PSOCKADDR RemoteAddress,
    PWSK_SOCKET AcceptSocket,
    PVOID *AcceptSocketContext,
    CONST WSK_CLIENT_CONNECTION_DISPATCH **AcceptSocketDispatch
    )
{
  PWSK_APP_SOCKET_CONTEXT SocketContext;

  // Check for a valid new socket
  if (AcceptSocket != NULL)
  {
    // Allocate the socket context
    SocketContext =
      (PWSK_APP_SOCKET_CONTEXT)
        ExAllocatePoolWithTag(
          NonPagedPool,
          sizeof(WSK_APP_SOCKET_CONTEXT),
          SOCKET_CONTEXT_POOL_TAG
          );

    // Check result of allocation
    if (SocketContext == NULL)
    {
      // Reject the socket
      return STATUS_REQUEST_NOT_ACCEPTED;
    }

    // Initialize the socket context
    SocketContext->Socket = AcceptSocket;
    ...

    // Set the accepted socket's client context
    *AcceptSocketContext = SocketContext;

    // Set the accepted socket's dispatch table of callback functions
    *AcceptSocketDispatch = ConnectionDispatch;

    // Perform additional operations on the accepted socket
    ...

    // Return status indicating that the socket was accepted
    return STATUS_SUCCESS:
  }

  // Error with listening socket
  else
  {
    // Handle error
    ...

    // Return status indicating that no socket was accepted
    return STATUS_REQUEST_NOT_ACCEPTED;
  }
}

Una aplicación WSK puede configurar un socket de escucha para aceptar condicionalmente las conexiones entrantes que se reciben en el socket. Una aplicación WSK habilita el modo de aceptación condicional en un socket de escucha estableciendo la opción de socket SO_CONDITIONAL_ACCEPT para el socket antes de enlazar el socket a una dirección de transporte local. Para obtener más información sobre cómo establecer las opciones de socket, vea Realizar operaciones de control en un socket.

Si el modo de aceptación condicional está habilitado en un socket de escucha, el subsistema WSK llama primero a la función de devolución de llamada de eventos WskInspectEvent del socket cada vez que se recibe una nueva solicitud de conexión entrante en el socket. La función de devolución de llamada de eventos WskInspectEvent de un socket puede inspeccionar la solicitud de conexión entrante para determinar si se debe aceptar o rechazar la solicitud. Para aceptar la solicitud, la función de devolución de llamada de evento WskInspectEvent del socket devuelve InspectAccept. Para rechazar la solicitud, la función de devolución de llamada de evento WskInspectEvent del socket devuelve InspectReject. Si la función de devolución de llamada de eventos WskInspectEvent de un socket no puede determinar inmediatamente si se debe aceptar o rechazar la solicitud, devuelve InspectPend. En esta situación, una aplicación WSK debe llamar a la función WskInspectComplete después de completar el proceso de inspección de la solicitud de conexión entrante. Si se quita una solicitud de conexión entrante antes de que la conexión de socket esté totalmente establecida, el subsistema WSK llama a la función de devolución de llamada de eventos WSKAbortEvent de la aplicación WSK.

En el ejemplo de código siguiente se muestra cómo una aplicación WSK puede inspeccionar una solicitud de conexión entrante mediante el subsistema WSK que llama a la función de devolución de llamada de eventos WskInspectEvent del socket de escucha.

// Inspect ID for a pending inspection
WSK_INSPECT_ID PendingInspectID

// A listening socket's WskInspectEvent event callback function
WSK_INSPECT_ACTION WSKAPI
  WskInspectEvent(
    PVOID SocketContext,
    PSOCKADDR LocalAddress,
    PSOCKADDR RemoteAddress,
    PWSK_INSPECT_ID InspectID
    )
{
  // Check for a valid inspect ID
  if (InspectID != NULL)
  {
    // Inspect local and/or remote address of the incoming
    // connection request to determine if the connection should
    // be accepted or rejected.
    ...

    // If the incoming connection should be accepted
    if (...)
    {
      // Return status indicating that the incoming
      // connection request was accepted
      return InspectAccept;
    }

    // If the incoming connection should be rejected
    else if (...)
    {
      // Return status indicating that the incoming
      // connection request was rejected
      return InspectReject;
    }

    // Cannot determine immediately
    else
    {
      // Save the inspect ID while the inspection is pending.
      // This will be passed to WskInspectComplete when the
      // inspection process is completed.
      PendingInspectID = *InspectID;

      // Return status indicating that the result of the
      // inspection process for the incoming connection
      // request is pending
      return InspectPend;
    }
  }

  // Error with listening socket
  else
  {
    // Handle error
    ...

    // Return status indicating that a socket was not accepted
    return InspectReject;
  }
}

// A listening socket's WskAbortEvent event callback function
NTSTATUS WSKAPI
  WskAbortEvent(
    PVOID SocketContext,
    PWSK_INSPECT_ID InspectID
    )
{
  // Terminate the inspection for the incoming connection
  // request with a matching inspect ID. To test for a matching
  // inspect ID, the contents of the WSK_INSPECT_ID structures
  // must be compared, not the pointers to the structures.
  ...
}

Si una aplicación WSK determina que aceptará una solicitud de conexión entrante en un socket de escucha que tenga habilitado el modo de aceptación condicional, se establecerá la conexión entrante y la aplicación que llama a la función WskAccept o el subsistema WSK que llama a la función de devolución de llamada de evento WskAcceptEvent del socket, como se ha descrito anteriormente.