Écoute et acceptation des connexions entrantes
Une fois qu’une application WSK (Winsock Kernel) lie un socket d’écoute à une adresse de transport locale, le socket commence à écouter les connexions entrantes à partir d’adresses de transport distantes. Une application WSK peut accepter une connexion entrante sur un socket d’écoute en appelant la fonction WskAccept . L’IRP que l’application transmet à la fonction WskAccept est mise en file d’attente jusqu’à ce qu’une connexion entrante arrive.
L’exemple de code suivant montre comment une application WSK peut accepter une connexion entrante en appelant la fonction 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;
}
En guise d’alternative à l’appel de la fonction WskAccept pour accepter les connexions entrantes sur un socket d’écoute, une application WSK peut activer la fonction de rappel d’événement WskAcceptEvent sur le socket. Si une application WSK active la fonction de rappel d’événement WskAcceptEvent sur un socket d’écoute, le sous-système WSK appelle la fonction de rappel d’événement WskAcceptEvent du socket chaque fois qu’une nouvelle connexion entrante est acceptée sur le socket. Pour plus d’informations sur l’activation de la fonction de rappel d’événement WskAcceptEvent d’un socket d’écoute, consultez Activation et désactivation des fonctions de rappel d’événements.
L’exemple de code suivant montre comment une application WSK peut accepter une connexion entrante par le sous-système WSK appelant la fonction de rappel d’événement WskAcceptEvent d’un socket d’écoute.
// 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;
}
}
Une application WSK peut configurer un socket d’écoute pour accepter de manière conditionnelle les connexions entrantes reçues sur le socket. Une application WSK active le mode d’acceptation conditionnelle sur un socket d’écoute en définissant l’option de socket SO_CONDITIONAL_ACCEPT pour le socket avant de lier le socket à une adresse de transport locale. Pour plus d’informations sur la définition des options de socket, consultez Exécution d’opérations de contrôle sur un socket.
Si le mode d’acceptation conditionnelle est activé sur un socket d’écoute, le sous-système WSK appelle d’abord la fonction de rappel d’événement WskInspectEvent du socket chaque fois qu’une nouvelle demande de connexion entrante est reçue sur le socket. La fonction de rappel d’événement WskInspectEvent d’un socket peut inspecter la demande de connexion entrante pour déterminer si la demande doit être acceptée ou rejetée. Pour accepter la demande, la fonction de rappel d’événement WskInspectEvent du socket renvoie InspectAccept. Pour rejeter la demande, la fonction de rappel d’événement WskInspectEvent du socket retourne InspectReject. Si la fonction de rappel d’événement WskInspectEvent d’un socket ne peut pas déterminer immédiatement si la demande doit être acceptée ou rejetée, elle retourne InspectPend. Dans ce cas, une application WSK doit appeler la fonction WskInspectComplete après avoir terminé le processus d’inspection de la demande de connexion entrante. Si une demande de connexion entrante est supprimée avant que la connexion de socket soit entièrement établie, le sous-système WSK appelle la fonction de rappel d’événement WskAbortEvent de l’application WSK.
L’exemple de code suivant montre comment une application WSK peut inspecter une demande de connexion entrante par le sous-système WSK appelant la fonction de rappel d’événement WskInspectEvent du socket d’écoute.
// 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.
...
}
Si une application WSK détermine qu’elle acceptera une demande de connexion entrante sur un socket d’écoute pour lequel le mode d’acceptation conditionnelle est activé, la connexion entrante est établie et elle peut être acceptée normalement par l’application appelant la fonction WskAccept ou le sous-système WSK appelant la fonction de rappel d’événement WskAcceptEvent du socket, comme décrit précédemment.