winsock kernel

winsock kernel支持多種類型的傳輸協定,WSK對象是client 和 socket。

基本介紹

  • 外文名:winsock kernel
  • 核心:WSK子系統
  • WSK對象:client 和 socket
  • 支持:多種類型的傳輸協定
WSK架構,WSK對象,客戶對象(Client Object),套接字對象(Socket Object),WSK套接字類別,基本套接字,監聽套接字,數據包套接字,基於連線的套接字,使用WSK核心函式,使用事件回調函式,WSK“擴展接口”,在Winsock核心函式中使用IRP,

WSK架構

WSK架構的核心是WSK子系統.WSK子系統是一個網路模組,它實現了WSK網路編程接口(NPI)的提供端(provider side)。WSK子系統接口在它的下層有傳輸接口,它支持多種類型的傳輸協定
綁定在WSK子系統上的是WSK程式。WSK程式是核心模式的軟體模組,為了展示了網路IO操作,它們實現了WSK NPI的客戶端。(在此處,“客戶”不應和客戶-伺服器系統中的“客戶”相混淆)WSK子系統可以調用WSK客戶NPI函式來通知WSK程式異步事件的到來。 WSK程式通過一組WSK註冊函式找到(discover)並綁定到WSK子系統。WSK程式可以通過使用這些函式在WSK子系統可用時動態檢測到它,並交換提供者和WSK NPI客戶端實現的派遣表。and to exchange dispatch tables that constitute the provider and client side implementations of the WSK NPI.
winsock kernel
WSK程式可以使用網路模組註冊(Network Module Registrar (NMR))綁定到WSK子系統。更多信息,參考“使用NMR實現WSK的註冊和註銷”Using NMR for WSK Registration and Unregistration.

WSK對象

Winsock 核心NPI圍繞兩個主要的對象類型來設計:client 和 socket。

客戶對象(Client Object)

一個client對象代表了一個WSK程式和WSK子系統間的依附或者綁定。一個Client對象 由一個WSK_CLIENT結構代表。一個指向客戶對象的指針在WSK程式綁定到WSK子系統時獲得。WSK程式傳遞這個指針給在這個客戶對象層面上操作的所有WSK函式。

套接字對象(Socket Object)

一個socket對象代表了一個可以用於網路I/O的網路套接字。一個Socket對象由一個WSK_SOCKET結構代表。一個指向套接字對象的指針在WSK程式接收一個進來的(incoming)連線時獲得。WSK程式傳遞這個指針給僅限於某個特定套接字的所有WSK函式。

WSK套接字類別

Winsock核心NPI定義了四種不同的套接字類別:基本套接字,監聽套接字,數據包套接字,基於連線的套接字。每個WSK套接字類別有特有的功能並支持不同的套接字函式集合。WSK程式必須在它創建一個新套接字對象時指定類別。每個WSK套接字類別的用途如下:

基本套接字

基本套接字(Basic Sockets)只用於獲取和設定傳輸堆疊的套接字選項,或者實現套接字IO控制操作。基本套接字不能綁定到本地的傳輸地址而且不能支持收發網路數據

監聽套接字

監聽套接字(Listening Sockets)用於監聽遠端(remote)傳輸地址的進入(incoming)連線。監聽套接字的功能包含所有基本套接字的功能。

數據包套接字

數據包套接字(Datagram Sockets)用於收發數據包(datagram)。數據包套接字的功能包含所有基本套接字的功能。

基於連線的套接字

基於連線的套接字(Connection-Oriented Sockets)用於在已建立的連線上收發網路數據。基於連線的套接字的功能包含所有基本套接字的功能。

使用WSK核心函式

WSK程式驅動套接字操作,也就是說當套接字造作發生時WSK程式開始控制。這會簡化WSK程式所請求的的同步。WSK程式給套接字函式提供IRP。這些IRP被WSK子系統排隊,直到套接字操作完成。更多關於WSK函式中使用IRP的信息,參考“WSK函式中使用IRP”(Using IRPs with Winsock Kernel Functions)。WSK程式可以實現阻塞套接字操作,通過等待IRP的每個操作都被WSK子系統完成。WSK程式可能需要進行多個在某些情況下排隊的套接字操作,為了在基於連線的套接字中確保有好的數據傳輸表現,為了在數據包套接字中防止數據包被丟棄,或者為了在監聽套接字中防止進入的連線被丟棄。WSK程式為數據傳輸操作提供數據緩衝區。這減少了數據被拷貝的次數。然而,如果一個WSK程式進行多個排隊的數據傳輸操作,它必須為每個排隊的數據傳輸操作提供數據緩衝區給WSK子系統。

使用事件回調函式

WSK子系統驅動套接字操作,意思是WSK子系統通過調用套接字的事件回調函式通知WSK程式套接字事件。WSK程式可能需要更複雜的同步來處理事件回調函式的異步特徵。WSK程式在套接字操作中不適用IRPs。WSK程式不需要排隊套接字操作。WSK子系統在套接字事件發生時,立刻調用WSK程式的事件回調函式。如果WSK程式可以與套接字事件回調函式被調用的進度持平,使用事件回調函式何以提供最好的表現和最小丟失數據包和進入的連線的機會。WSK子系統為數據傳輸操作提供數據緩衝區。WSK程式必須立刻或者在一個合理的時間內釋放這些數據緩衝區給WSK子系統,這可以避免WSK子系統用完存儲資源。因此,WSK程式可能需要把數據從WSK子系統擁有的數據緩衝區拷貝到它自己的數據緩衝區。

WSK“擴展接口”

WSK NPI支持擴展接口。WSK子系統可以通過使用“擴展接口”擴展WSK套接字的功能,是它不僅限於WSK NPI當前定義的套接字功能集和事件回調函式。每個NPI定義的擴展接口與WSK NPI無關。當前還沒有沒有定義擴展接口。
WSK程式可以通過使用SIO_WSK_REGISTER_EXTENSION套接字IOCTL操作註冊一個被WSK子系統支持的設備擴展。
更多關於註冊擴展接口的信息,參考“註冊一個設備擴展” (Registering an Extension Interface)。

在Winsock核心函式中使用IRP

WSK NPI使用IRP實現網路I/O操作的異步完成。IRP的指針作為每個WSK函式的一個參數。WSK子系統在WSK函式完成操作後完成此IRP。
WSK程式用來傳遞到一個WSK函式中的IRP可以源於以下方法。
WSK程式調用IoAllocateIrp分配這個IRP。這種情況下,WSK程式必須給這個IRP分配至少一個I/O堆疊單元。WSK程式重新使用一個先前分配但已完成的IRP。WSK必須調用IoReuseIrp來重新初始化這個IRP。WSK程式使用從上層或I/O管理器傳到它的IRP。這種情況,此IRP必須有至少一個IO堆疊單元供WSK子系統使用。
在WSK程式擁有一個IRP來調用WSK函式後,它可以為此IRP設定一個完成例程,當WSK子系統完成IRP時此例程被調用。WSK程式通過調用IoSetCompletionRoutine來為IRP設定完成例程。IoCompletion routine需要或可選,取決於這個IRP是如何產生的。
如果WSK程式分配此IRP,或者重用它先前分配的IRP,那么它必須在調用WSK函式前設定一個完成例程。這種情況下,必須把傳遞給IoCompletion routine的參數InvokeOnSuccess, InvokeOnError, and InvokeOnCancel都設為TRUE,以確保IoCompletion routine總是被調用。而且,此IRP的IoCompletion routine必須總是返回STATUS_MORE_PROCESSING_REQUIRED來結束此IRP的完成處理。If the WSK application is done using the IRP after the IoCompletion routine has been called, then it should call the IoFreeIrp function to free the IRP before returning from the IoCompletion routine.如果WSK程式。如果WSK程式沒有釋放這個IRP,那么它可以在調用其它的WSK函式時重用這個IRP。如果WSK程式使用一個從高層驅動或IO管理器傳遞下來的IRP,僅當它在WSK函式完成操作後必須被通知時,它需要在調用WSK函式前設定完成例程。如果WSK程式沒有為此IRP設定完成例程,當此IRP被完成時,它會像每個正常IRP的完成處理那樣被傳遞至上層驅動或IO管理器。如果WSK程式設定了一個完成例程,這個完成例程可以返回STATUS_SUCCESS或STATUS_MORE_PROCESSING_REQUIRED。如果完成例程返回了STATUS_SUCCESS,這個IRP的完成處理會正常地繼續下去。如果完成例程返回了STATUS_MORE_PROCESSING_REQUIRED,WSK程式必須在它完成WSK函式處理的操作完成之後調用函式IoCompleteRequest來完成此IRP。WSK程式不能釋放從高層驅動或IO管理器傳給它的IRP。
注意:如果WSK程式為由上層驅動或IO管理器傳遞過來的且傳至下層的IRP設定了一個完成例程,那么完成例程必須檢查IRP的PendingReturned位,如果PendingReturned是TRUE的話,它必須調用IoMarkIrpPending。更多信息,參考“實現一個完成例程” Implementing an IoCompletion Routine。
WSK程式不初始化它傳遞至WSK函式的IRP,除了設定一個完成例程。當WSK程式傳遞一個IRP到WSK函式時,WSK子系統代表WSA程式設定下一堆疊單元。
下面的代碼示例顯示了WSK程式在一個套接字上進行接收操作時如何分配和使用IRP。
// 接受完成例程的原型
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
);
// 接收數據的函式
NTSTATUS
ReceiveData(
PWSK_SOCKET Socket,
PWSK_BUF DataBuffer
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
PIRP Irp;
NTSTATUS Status;
// 獲取“供應者派遣結構指針”
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// 分配一個IRP
Irp =
IoAllocateIrp(
1,
FALSE
);
// Check result
if (!Irp)
{
// 返回錯誤
return STATUS_INSUFFICIENT_RESOURCES;
}
// 設定此IRP的完成例程
IoSetCompletionRoutine(
Irp,
ReceiveComplete,
DataBuffer, // 數據緩衝區作為上下文
TRUE,
TRUE,
TRUE
);
// 初始化這個套接字的接收操作
Status =
Dispatch->WskReceive(
Socket,
DataBuffer,
0, // 沒有指定標誌
Irp
);
// 返回WskReceive的狀態
return Status;
}
// 接收完成例程
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PWSK_BUF DataBuffer;
ULONG ByteCount;
// 檢查接收操作的結果
if (Irp->IoStatus.Status == STATUS_SUCCESS)
{
// 獲取數據緩衝區的指針
DataBuffer = (PWSK_BUF)Context;
// 獲取接收的位元組數
ByteCount = (ULONG)(Irp->IoStatus.Information);
// 處理接收的數據
...
}
// 錯誤狀態
else
{
// 處理錯誤
...
}
// 釋放IRP
IoFreeIrp(Irp);
// 一直返回STATUS_MORE_PROCESSING_REQUIRED 來
// 結束IRP的完成處理
return STATUS_MORE_PROCESSING_REQUIRED;
}
在上面例子顯示的模型中,WSK程式分配IRP並在完成例程里釋放它。在餘下的WSK文檔中的例子使用了這個模型。
下面的代碼示例顯示了,在套接字上實現接受操作時,WSK程式可以使用上層驅動或IO管理器傳遞給它的IRP,當
// 接收完成例程的原型
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
);
//接收數據的函式
NTSTATUS
ReceiveData(
PWSK_SOCKET Socket,
PWSK_BUF DataBuffer,
PIRP Irp; //從上層驅動或IO管理器傳遞下來的Irp
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
NTSTATUS Status;
// 獲取“供應者派遣表”的指針
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// 設定IRP的完成例程,僅當接收操作成功後被調用
IoSetCompletionRoutine(
Irp,
ReceiveComplete,
DataBuffer, // 數據緩衝區作為上下文
TRUE,
FALSE,
FALSE
);
// 在套接字上初始化接收操作
Status =
Dispatch->WskReceive(
Socket,
DataBuffer,
0, // 未指定標誌
Irp
);
// 返回 WskReceive()的狀態碼
return Status;
}
// 接收完成例程
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PWSK_BUF DataBuffer;
ULONG ByteCount;
// 因為完成例程只在操作完成時調用,所以這個永遠是TRUE
ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);
// 檢查這個IRP的pending狀態
if (Irp->PendingReturned == TRUE)
{
// 標記這個IRP為pending
IoMarkIrpPending(Irp);
}
// 獲取數據緩衝區的指針
DataBuffer = (PWSK_BUF)Context;
// 獲取接收的位元組數
ByteCount = (ULONG)(Irp->IoStatus.Information);
// 處理接收的數據
...
// 返回STATUS_SUCCESS繼續此IRP的完成處理
return STATUS_SUCCESS;
}
更多關於使用IRP的信息,參考“處理IRP” Handling IRPs。

相關詞條

熱門詞條

聯絡我們