Прослушивание и прием входящих подключений
Когда приложение Winsock Kernel (WSK) привязывает сокет прослушивания к локальному транспортному адресу, сокет начинает ожидать входящих подключений с удаленных транспортных адресов. Приложение WSK может принимать входящие подключения в сокет прослушивания, вызвав функцию WskAccept. IRP, который приложение передает функции WskAccept, ставится в очередь в ожидании входящего подключения.
В следующем примере кода показано, как приложение 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 вызывает функцию обратного вызова события приложения WskAbortEvent.
В следующем примере кода показано, как приложение WSK может проверять входящие запросы на подключение при помощи подсистемы WSK, вызывающей функцию обратного вызова событий слушающего сокета WskInspectEvent.
// 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, либо подсистемой WSK, которая вызывает функцию обратного вызова события WskAcceptEvent, как описано ранее.