Share via


수신 연결 수신 대기 및 수락

Winsock Kernel(WSK) 애플리케이션이 수신 대기 소켓을 로컬 전송 주소에 바인딩한 후 소켓은 원격 전송 주소에서 들어오는 연결을 수신 대기하기 시작합니다. WSK 애플리케이션은 WskAccept 함수를 호출하여 수신 대기 소켓에서 들어오는 연결을 수락할 수 있습니다. 애플리케이션이 WskAccept 함수에 전달하는 IRP는 들어오는 연결이 도착할 때까지 큐에 대기됩니다.

다음 코드 예제에서는 WSK 애플리케이션이 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;
}

수신 대기 소켓에서 들어오는 연결을 수락하기 위해 WskAccept 함수를 호출하는 대신 WSK 애플리케이션은 소켓에서 WskAcceptEvent 이벤트 콜백 함수를 사용하도록 설정할 수 있습니다. WSK 애플리케이션이 수신 대기 소켓에서 WskAcceptEvent 이벤트 콜백 함수를 사용하도록 설정하는 경우 WSK 하위 시스템은 소켓에서 새 들어오는 연결이 수락 될 때마다 소켓의 WskAcceptEvent 이벤트 콜백 함수를 호출합니다. 수신 대기 소켓의 WskAcceptEvent 이벤트 콜백 함수를 사용하도록 설정하는 방법에 대한 자세한 내용은 이벤트 콜백 함수 사용 및 비활성화를 참조하세요.

다음 코드 예제에서는 WSK 애플리케이션이 수신 대기 소켓의 WskAcceptEvent 이벤트 콜백 함수를 호출하는 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 하위 시스템에 의해 정상적으로 수락될 수 있습니다.