着信接続のリッスンと受け入れ

Winsock カーネル (WSK) アプリケーションがリスニング ソケットをローカル トランスポート アドレスにバインドすると、ソケットはリモート トランスポート アドレスからの受信接続のリスニングを開始します。 WSK アプリケーションは、WskAccept 関数を呼び出すことによって、リスニング ソケット上の受信接続を受け入れます。 アプリケーションが WskAccept 関数に渡す IRP は、受信接続が到着するまでキューに入れられます。

次のコード例は、WskAccept 関数を呼び出すことによって WSK アプリケーションが受信接続を受け入れる方法を示しています。

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

WskAccept 関数を呼び出してリッスン ソケットで受信接続を受け入れる代わりに、WSK アプリケーションがソケットで WskAcceptEvent イベント コールバック関数を有効にできます。 WSK アプリケーションがリスニング ソケットで WskAcceptEvent イベント コールバック関数を有効にした場合、WSK サブシステムは、ソケットで新しい受信接続が受け入れられるたびに、ソケットの WskAcceptEvent イベント コールバック関数を呼び出します。 リスニング ソケットの WskAcceptEvent イベント コールバック関数の有効化の詳細については、「イベント コールバック関数の有効化と無効化」を参照してください。

次のコード例は、リスニング ソケットの WskAcceptEvent イベント コールバック関数を呼び出す WSK サブシステムによる受信接続を WSK アプリケーションが受け入れる方法を示しています。

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

WSK アプリケーションは、ソケットで受信した受信接続を条件付きで受け入れるようにリスニング ソケットを構成できます。 WSK アプリケーションでは、ソケットをローカル トランスポート アドレスにバインドする前にソケットの SO_CONDITIONAL_ACCEPT ソケット オプションを設定することで、リスニング ソケットで条件付き受け入れモードを有効にします。 ソケット オプションを設定する方法の詳細については、「ソケットでの制御オペレーションの実行」を参照してください。

リスニング ソケットで条件付き受け入れモードが有効になっている場合、WSK サブシステムは、新しい受信接続要求がソケットで受信されるたびに、まずソケットの WskInspectEvent イベント コールバック関数を呼び出します。 ソケットの WskInspectEvent イベント コールバック関数は、受信接続要求を検査して、要求を受け入れるか拒否するかを判断できます。 要求の受け入れにおいて、ソケットの WskInspectEvent イベント コールバック関数は InspectAccept を返します。 要求の拒否において、ソケットの WskInspectEvent イベント コールバック関数は InspectReject を返します。 ソケットの WskInspectEvent イベント コールバック関数は、要求を受け入れるか拒否するかをすぐに判断できない場合、InspectPend を返します。 この状況で、WSK アプリケーションは、受信接続要求の検査プロセスを完了した後、WskInspectComplete 関数を呼び出す必要があります。 ソケット接続が完全に確立される前に受信接続要求が削除された場合、WSK サブシステムは WSK アプリケーションの WskAbortEvent イベント コールバック関数を呼び出します。

次のコード例は、WSK アプリケーションがリスニング ソケットの WskInspectEvent イベント コールバック関数を呼び出す WSK サブシステムによる受信接続要求を検査する方法を示しています。

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

条件付き受け入れモードが有効になっているリスニング ソケットで受信接続要求を受け入れると WSK アプリケーションが判断した場合、受信接続が確立され、WskAccept 関数を呼び出すアプリケーション、または前述のようにソケットの WskAcceptEvent イベント コールバック関数を呼び出す WSK サブシステムによって通常受け入れられます。