Condividi tramite


Ascolto e accettazione di connessioni in ingresso

Dopo che un'applicazione WSK (Winsock Kernel) associa un socket di ascolto a un indirizzo di trasporto locale, il socket inizia ad ascoltare le connessioni in ingresso da indirizzi di trasporto remoto. Un'applicazione WSK può accettare una connessione in ingresso su un socket di ascolto chiamando la funzione WskAccept . L'IRP che l'applicazione passa alla funzione WskAccept viene accodata fino all'arrivo di una connessione in ingresso.

Nell'esempio di codice seguente viene illustrato come un'applicazione WSK può accettare una connessione in ingresso chiamando la funzione 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;
}

In alternativa alla chiamata alla funzione WskAccept per accettare connessioni in ingresso su un socket di ascolto, un'applicazione WSK può abilitare la funzione di callback dell'evento WskAcceptEvent sul socket. Se un'applicazione WSK abilita la funzione di callback dell'evento WskAcceptEvent su un socket in ascolto, il sottosistema WSK chiama la funzione di callback dell'evento WskAcceptEvent del socket ogni volta che viene accettata una nuova connessione in ingresso nel socket. Per altre informazioni sull'abilitazione della funzione di callback dell'evento WskAcceptEvent di un socket in ascolto, vedere Abilitazione e disabilitazione delle funzioni di callback degli eventi.

L'esempio di codice seguente illustra come un'applicazione WSK può accettare una connessione in ingresso dal sottosistema WSK che chiama la funzione di callback dell'evento WskAcceptEvent di un socket di ascolto.

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

Un'applicazione WSK può configurare un socket in ascolto per accettare in modo condizionale le connessioni in ingresso ricevute nel socket. Un'applicazione WSK abilita la modalità di accettazione condizionale su un socket di ascolto impostando l'opzione SO_CONDITIONAL_ACCEPT socket per il socket prima di eseguire il binding del socket a un indirizzo di trasporto locale. Per altre informazioni su come impostare le opzioni socket, vedere Esecuzione di operazioni di controllo su un socket.

Se la modalità di accettazione condizionale è abilitata in un socket di ascolto, il sottosistema WSK chiama innanzitutto la funzione di callback dell'evento WskInspectEvent del socket ogni volta che viene ricevuta una nuova richiesta di connessione in ingresso nel socket. La funzione di callback dell'evento WskInspectEvent di un socket può esaminare la richiesta di connessione in ingresso per determinare se la richiesta deve essere accettata o rifiutata. Per accettare la richiesta, la funzione di callback dell'evento WskInspectEvent del socket restituisce InspectAccept. Per rifiutare la richiesta, la funzione di callback dell'evento WskInspectEvent del socket restituisce InspectReject. Se la funzione di callback dell'evento WskInspectEvent di un socket non può determinare immediatamente se la richiesta deve essere accettata o rifiutata, restituisce InspectPend. In questo caso, un'applicazione WSK deve chiamare la funzione WskInspectComplete dopo aver completato il processo di ispezione per la richiesta di connessione in ingresso. Se una richiesta di connessione in ingresso viene eliminata prima che la connessione socket venga stabilita completamente, il sottosistema WSK chiama la funzione di callback dell'evento WskAbortEvent dell'applicazione WSK.

Nell'esempio di codice seguente viene illustrato come un'applicazione WSK può esaminare una richiesta di connessione in ingresso dal sottosistema WSK che chiama la funzione di callback dell'evento WskInspectEvent del socket in ascolto.

// 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 un'applicazione WSK determina che accetterà una richiesta di connessione in ingresso su un socket di ascolto con modalità di accettazione condizionale abilitata, la connessione in ingresso verrà stabilita e può essere accettata normalmente dall'applicazione che chiama la funzione WskAccept o il sottosistema WSK che chiama la funzione di callback dell'evento WskAcceptEvent del socket come descritto in precedenza.