Включение и отключение функций обратного вызова событий

Приложение Winsock Kernel (WSK) может реализовать функции обратного вызова событий, которые подсистема WSK вызывает асинхронно, чтобы уведомить приложение, когда определенные события происходят в сокете. Приложение WSK может предоставить в подсистему WSK структуру таблицы распределения всякий раз, когда оно создает сокет или принимает соединение на прослушивающем сокете. Эта диспетчерская таблица содержит указатели на функции обратного вызова событий приложения WSK для нового сокета. Если приложение WSK не реализует никаких функций обратного вызова событий для определенного сокета, то не требуется предоставлять структуру таблицы диспетчеризации клиента подсистеме WSK для этого сокета.

Все функции обратного вызова событий сокета, за исключением функций обратного вызова WskInspectEvent и WskAbortEvent для слушающего сокета, можно включить или отключить с помощью параметра сокета SO_WSK_EVENT_CALLBACK. Приложение WSK может включить несколько функций обратного вызова событий в сокете одновременно. Однако приложение WSK должно отключить каждую функцию обратного вызова событий по отдельности.

В следующем примере кода показано, как приложение WSK может использовать параметр сокета SO_WSK_EVENT_CALLBACK, чтобы включить функции обратного вызова для событий WskDisconnectEvent и WskReceiveEvent на подключенном сокете.

// Function to enable the WskDisconnectEvent and WskReceiveEvent
// event callback functions on a connection-oriented socket
NTSTATUS
  EnableDisconnectAndRecieveCallbacks(
    PWSK_SOCKET Socket
    )
{
  PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
  WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
  NTSTATUS Status;

  // Get pointer to the socket's provider dispatch structure
  Dispatch =
    (PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);

  // Specify the WSK NPI identifier
  EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;

  // Set the event flags for the event callback functions that
  // are to be enabled on the socket
  EventCallbackControl.EventMask =
    WSK_EVENT_DISCONNECT | WSK_EVENT_RECEIVE;

  // Initiate the control operation on the socket
  Status =
    Dispatch->WskControlSocket(
      Socket,
      WskSetOption,
      SO_WSK_EVENT_CALLBACK,
      SOL_SOCKET,
      sizeof(WSK_EVENT_CALLBACK_CONTROL),
      &EventCallbackControl,
      0,
      NULL,
      NULL,
      NULL  // No IRP for this control operation
      );

  // Return the status of the call to WskControlSocket()
  return Status;
}

В следующем примере кода показано, как приложение WSK может использовать параметр сокета SO_WSK_EVENT_CALLBACK для отключения функций обратного вызова событий WskReceiveEvent в сокете, ориентированном на подключение.

// Prototype for the disable disconnect IoCompletion routine
NTSTATUS
  DisableDisconnectComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    );

// Function to disable the WskDisconnectEvent event
// callback functions on a connection-oriented socket
NTSTATUS
  DisableDisconnectCallback(
    PWSK_SOCKET Socket
    )
{
  PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
  PIRP Irp;
  WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
  NTSTATUS Status;

  // Get pointer to the socket's provider dispatch structure
  Dispatch =
    (PWSK_PROVIDER_CONNECTION_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,
 DisableDisconnectComplete,
    Socket,  // Use the socket object for the context
    TRUE,
    TRUE,
    TRUE
    );

  // Specify the WSK NPI identifier
  EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;

  // Set the event flag for the event callback function that
  // is to be disabled on the socket along with the disable flag
  EventCallbackControl.EventMask =
    WSK_EVENT_DISCONNECT | WSK_EVENT_DISABLE;

  // Initiate the control operation on the socket
  Status =
    Dispatch->WskControlSocket(
      Socket,
      WskSetOption,
      SO_WSK_EVENT_CALLBACK,
      SOL_SOCKET,
      sizeof(WSK_EVENT_CALLBACK_CONTROL),
      &EventCallbackControl,
      0,
      NULL,
      NULL,
      Irp
      );

  // Return the status of the call to WskControlSocket()
  return Status;
}

// Disable disconnect IoCompletion routine
NTSTATUS
  DisableDisconnectComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
  UNREFERENCED_PARAMETER(DeviceObject);

  PWSK_SOCKET Socket;

  // Check the result of the control operation
  if (Irp->IoStatus.Status == STATUS_SUCCESS)
  {
    // The WskDisconnectEvent event callback
    // function is now disabled

    // Get the socket object from the context
    Socket = (PWSK_SOCKET)Context;

    // Perform the next operation on the 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;
}

Для прослушивающих сокетов функции обратного вызова событий WskInspectEvent и WskAbortEvent активируются только если приложение WSK включает режим условного принятия на сокете. Приложение WSK включает режим условного принятия на сокете прослушивания, задавая параметр SO_CONDITIONAL_ACCEPT для сокета перед привязкой к локальному транспортному адресу. Дополнительные сведения о настройке параметров сокета см. в разделе о выполнении управляющих операций на сокете.

После того как для сокета прослушивания включен условный режим принятия, функции обратного вызова событий WskInspectEvent и WskAbortEvent не могут быть отключены. Дополнительные сведения об условном принятии входящих подключений для прослушивающих сокетов см. в разделе Прослушивание и принятие входящих подключений.

Слушающий сокет может автоматически включать функции обратного вызова событий для соединительных сокетов, которые принимаются функцией обратного вызова событий WskAcceptEvent этого слушающего сокета. Приложение WSK автоматически включает эти функции обратного вызова путем включения функций обратного вызова событий, ориентированных на подключение, в сокете прослушивания. Дополнительные сведения об этом процессе см. в SO_WSK_EVENT_CALLBACK.

Если приложение WSK всегда включает определенные функции обратного вызова событий на каждом сокете, который оно создает, приложение может настроить подсистему WSK для автоматического включения этих функций обратного вызова событий с помощью операции управления клиентом WSK_SET_STATIC_EVENT_CALLBACKS. Функции обратного вызова событий, включенные таким образом, всегда включены и не могут быть отключены или повторно включены позже приложением WSK. Если приложение WSK всегда включает определенные функции обратного вызова событий на каждом сокете, который он создает, приложение должно использовать этот метод для автоматического включения этих функций обратного вызова событий, так как это обеспечит гораздо более высокую производительность.

В следующем примере кода показано, как приложение WSK может использовать операцию управления клиентом WSK_SET_STATIC_EVENT_CALLBACKS для автоматического включения функции обратного вызова событий WskReceiveFromEvent для сокетов данных и функции обратного вызова событий WskReceiveEvent для сокетов, ориентированных на подключение.

// Function to set static event callbacks
NTSTATUS
  SetStaticEventCallbacks(
    PWSK_APP_BINDING_CONTEXT BindingContext,
    )
{
  WSK_EVENT_CALLBACK_CONTROL EventCallbackControl;
  NTSTATUS Status;

  // Specify the WSK NPI identifier
  EventCallbackControl.NpiId = &NPI_WSK_INTERFACE_ID;

  // Set the event flags for the event callback functions that
  // are to be automatically enabled on every new socket
  EventCallbackControl.EventMask =
    WSK_EVENT_RECEIVE_FROM | WSK_EVENT_RECEIVE;

  // Perform client control operation
  Status =
    BindingContext->
      WskProviderDispatch->
        WskControlClient(
          BindingContext->WskClient,
          WSK_SET_STATIC_EVENT_CALLBACKS,
          sizeof(WSK_EVENT_CALLBACK_CONTROL),
          &EventCallbackControl,
          0,
          NULL,
          NULL,
          NULL  // No IRP for this control operation
          );

  // Return status of client control operation
  return Status;
}

Если приложение WSK использует операцию управления клиентом WSK_SET_STATIC_EVENT_CALLBACKS для автоматического включения определённых функций обратного вызова событий, оно должно сделать это до создания каких-либо сокетов.