Escutando e aceitando conexões de entrada

Depois que um aplicativo WSK (Winsock Kernel) associa um soquete de escuta a um endereço de transporte local, o soquete começa a escutar conexões de entrada de endereços de transporte remoto. Um aplicativo WSK pode aceitar uma conexão de entrada em um soquete de escuta chamando a função WskAccept . O IRP que o aplicativo passa para a função WskAccept é enfileirado até que uma conexão de entrada chegue.

O exemplo de código a seguir mostra como um aplicativo WSK pode aceitar uma conexão de entrada chamando a função 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 para chamar a função WskAccept para aceitar conexões de entrada em um soquete de escuta, um aplicativo WSK pode habilitar a função de retorno de chamada de evento WskAcceptEvent no soquete . Se um aplicativo WSK habilitar a função de retorno de chamada de evento WskAcceptEvent em um soquete de escuta, o subsistema WSK chamará a função de retorno de chamada de evento WskAcceptEvent do soquete sempre que uma nova conexão de entrada for aceita no soquete. Para obter mais informações sobre como habilitar a função de retorno de chamada de evento WskAcceptEvent de um soquete de escuta, consulte Habilitando e desabilitando funções de retorno de chamada de evento.

O exemplo de código a seguir mostra como um aplicativo WSK pode aceitar uma conexão de entrada pelo subsistema WSK chamando a função de retorno de chamada de evento WskAcceptEvent de um soquete de escuta.

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

Um aplicativo WSK pode configurar um soquete de escuta para aceitar condicionalmente conexões de entrada recebidas no soquete. Um aplicativo WSK permite o modo de aceitação condicional em um soquete de escuta definindo a opção de soquete SO_CONDITIONAL_ACCEPT para o soquete antes de associar o soquete a um endereço de transporte local. Para obter mais informações sobre como definir opções de soquete, consulte Executando operações de controle em um soquete.

Se o modo de aceitação condicional estiver habilitado em um soquete de escuta, o subsistema WSK primeiro chamará a função de retorno de chamada de evento WskInspectEvent do soquete sempre que uma nova solicitação de conexão de entrada for recebida no soquete. A função de retorno de chamada de evento WskInspectEvent de um soquete pode inspecionar a solicitação de conexão de entrada para determinar se a solicitação deve ser aceita ou rejeitada. Para aceitar a solicitação, a função de retorno de chamada de evento WskInspectEvent do soquete retorna InspectAccept. Para rejeitar a solicitação, a função de retorno de chamada de evento WskInspectEvent do soquete retorna InspectReject. Se a função de retorno de chamada de evento WskInspectEvent de um soquete não puder determinar imediatamente se a solicitação deve ser aceita ou rejeitada, ela retornará InspectPend. Nessa situação, um aplicativo WSK deve chamar a função WskInspectComplete depois de concluir o processo de inspeção para a solicitação de conexão de entrada. Se uma solicitação de conexão de entrada for descartada antes que a conexão de soquete seja totalmente estabelecida, o subsistema WSK chamará a função de retorno de chamada de evento WskAbortEvent do aplicativo WSK.

O exemplo de código a seguir mostra como um aplicativo WSK pode inspecionar uma solicitação de conexão de entrada pelo subsistema WSK chamando a função de retorno de chamada de evento WskInspectEvent do soquete de escuta.

// 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.
  ...
}

Se um aplicativo WSK determinar que aceitará uma solicitação de conexão de entrada em um soquete de escuta que tenha o modo de aceitação condicional habilitado, a conexão de entrada será estabelecida e poderá ser aceita normalmente pela chamada do aplicativo para a função WskAccept ou pelo subsistema WSK que chama a função de retorno de chamada de evento WskAcceptEvent do soquete , conforme descrito anteriormente.