接聽和接受連入連線
在 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 應用程式如何透過 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 事件回呼函式,如先前所述。