共用方式為


接聽和接受連入連線

在 Winsock 核心 (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 應用程式如何接受 WSK 子系統呼叫接聽通訊端的 WskAcceptEvent 事件回呼函式的傳入連線。

// 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 子系統接受,如先前所述。