Freigeben über


Überwachen und Akzeptieren eingehender Verbindungen

Nachdem eine WSK-Anwendung (Winsock Kernel) einen lauschenden Socket an eine lokale Transportadresse gebunden hat, beginnt der Socket mit dem Lauschen auf eingehende Verbindungen von Remotetransportadressen. Eine WSK-Anwendung kann eine eingehende Verbindung mit einem lauschenden Socket akzeptieren, indem sie die WskAccept-Funktion aufruft . Das IRP, das die Anwendung an die WskAccept-Funktion übergibt, wird in die Warteschlange eingereiht, bis eine eingehende Verbindung eingeht.

Das folgende Codebeispiel zeigt, wie eine WSK-Anwendung eine eingehende Verbindung durch Aufrufen der WskAccept-Funktion akzeptieren kann.

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

Als Alternative zum Aufrufen der WskAccept-Funktion , um eingehende Verbindungen auf einem Lauschocket zu akzeptieren, kann eine WSK-Anwendung die WskAcceptEvent-Ereignisrückruffunktion für den Socket aktivieren. Wenn eine WSK-Anwendung die WskAcceptEvent-Ereignisrückruffunktion in einem lauschenden Socket aktiviert, ruft das WSK-Subsystem die WskAcceptEvent-Ereignisrückruffunktion des Sockets auf, wenn eine neue eingehende Verbindung für den Socket akzeptiert wird. Weitere Informationen zum Aktivieren der WskAcceptEvent-Ereignisrückruffunktion eines lauschenden Sockets finden Sie unter Aktivieren und Deaktivieren von Ereignisrückruffunktionen.

Das folgende Codebeispiel zeigt, wie eine WSK-Anwendung eine eingehende Verbindung durch das WSK-Subsystem akzeptieren kann, das die WskAcceptEvent-Ereignisrückruffunktion eines lauschenden Sockets aufruft.

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

Eine WSK-Anwendung kann einen Lauschocket so konfigurieren, dass eingehende Verbindungen, die auf dem Socket empfangen werden, bedingt akzeptiert werden. Eine WSK-Anwendung aktiviert den Modus für die bedingte Akzeptanz für einen lauschenden Socket, indem die Option SO_CONDITIONAL_ACCEPT Sockets für den Socket festgelegt wird, bevor der Socket an eine lokale Transportadresse gebunden wird. Weitere Informationen zum Festlegen von Socketoptionen finden Sie unter Ausführen von Steuerungsvorgängen für einen Socket.

Wenn der bedingte Annahmemodus für einen Lauschocket aktiviert ist, ruft das WSK-Subsystem zuerst die WskInspectEvent-Ereignisrückruffunktion des Sockets auf, wenn eine neue eingehende Verbindungsanforderung für den Socket empfangen wird. Die WskInspectEvent-Ereignisrückruffunktion eines Sockets kann die eingehende Verbindungsanforderung untersuchen, um festzustellen, ob die Anforderung akzeptiert oder abgelehnt werden soll. Um die Anforderung zu akzeptieren, gibt die WskInspectEvent-Ereignisrückruffunktion des Sockets InspectAccept zurück. Um die Anforderung abzulehnen, gibt die WskInspectEvent-Ereignisrückruffunktion des Sockets InspectReject zurück. Wenn die WskInspectEvent-Ereignisrückruffunktion eines Sockets nicht sofort ermitteln kann, ob die Anforderung akzeptiert oder abgelehnt werden soll, wird InspectPend zurückgegeben. In diesem Fall muss eine WSK-Anwendung die WskInspectComplete-Funktion aufrufen, nachdem der Überprüfungsprozess für die eingehende Verbindungsanforderung abgeschlossen wurde. Wenn eine eingehende Verbindungsanforderung gelöscht wird, bevor die Socketverbindung vollständig hergestellt wird, ruft das WSK-Subsystem die WskAbortEvent-Ereignisrückruffunktion der WSK-Anwendung auf.

Das folgende Codebeispiel zeigt, wie eine WSK-Anwendung eine eingehende Verbindungsanforderung durch das WSK-Subsystem überprüfen kann, das die WskInspectEvent-Ereignisrückruffunktion des lauschenden Sockets aufruft.

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

Wenn eine WSK-Anwendung feststellt, dass sie eine eingehende Verbindungsanforderung für einen lauschenden Socket akzeptiert, für den der bedingte Annahmemodus aktiviert ist, wird die eingehende Verbindung hergestellt und kann entweder von der Anwendung akzeptiert werden, die die WskAccept-Funktion aufruft, oder das WSK-Subsystem, das die WskAcceptEvent-Ereignisrückruffunktion des Sockets aufruft, wie zuvor beschrieben.