- 注册时间
 - 2021-4-16
 
- 最后登录
 - 2024-11-26
 
- 在线时间
 - 4 小时
 
 
 
 
 
编程入门 
  
	- 龙马币
 - 154 
 
 
 
 
 | 
 
 
WSK 是一个内核模式下的网络编程接口(NPI).通过WSK,内核模式的软件模块可以使用同用户模式的Winsock2一样的网络操作。WSK NPI支持socket的创建,绑定,建立连接,数据的发送和接收等操作。虽然大多数操作是跟用户模式的socket一样,但是它也有一些特殊的特性,比如基于IRP的异步IO,事件回调来增强系统的表现。 
 
WSK只对于Windows Vista及其以后的操作系统,我们应该使用WSK替代TDI,因为其增强了系统的表现和更容易的编程。 
 
Winsock Kernel Overview 
Winsock Kernel Architecture 
 
这个体系的核心是WSK子系统。WSK子系统是一个网络模块提供WSK的NPI。WSK子系统接口和传输层提供层,在传输层的下边缘,提供对不同传输协议的支持。 
 
WSK的应用就附加在WSD子系统的上面。WSK应用是一些内核模式下的软件模块,为了执行网络IO操作,执行一些WSK NPI客户方面的操作。WSK可以调用WSK客户NPI, 
通知WSK应用,关于一些异步事件的发生。 
 
WSK应用通过使用WSK注册的函数来发现并附加到WSK子系统上。应用可以使用这些函数动态的探测WSK子系统什么时候可以使用,以及动态修改为客户提供的WSK NPI 
的配发函数表。 
 
WSK应用可以通过使用网络模块注册(NMR)附加在WSK子系统上。 
 
Winsock Kernel Objects 
 
Client Object 
客户对象代表在WSK应用和WSK子系统间的一个附件,或绑定。客户对象通过WSK_CLIENT结构表示。当WSK应用附加在WSK子系统后,会返回一个指向 
其结构体的指针。WSK应用对所有涉及到客户对象层次上的函数都会传递这个指针。 
 
 
Socket Object 
套接字对象代表可被用于网络IO操作的网络套接字。套接字对象通过WSK_SOCKET结构表示。当应用创建了一个新的套接字或接收到一个来自连接的套接字的时候, 
WSK应用返回一个指向该结构体的指针。WSK应用在使用指定套接字的函数时,传递指针给这些WSK函数。 
 
 
Winsock Kernel Socket Categories 
WSK定义了四种不同类型的套接字,基本套接字,监听套接字,数据报套接字,和面向连接的套接字。每一种类型的套接字都有唯一的功能和一组支持的函数。WSK应用在创建新的套接字的时候必须指定WSK套接字的类型。 
 
 
Basic Sockets: 
基本套接字仅仅只被用于获得并设置传输堆栈中的套接字的选项或执行套接字IO控制操作。基本套接字不能被绑定到本地传输地址上,也不能支持发送或接收网络数据。 
 
 
Listening Sockets: 
监听套接字被用来对远程传输地址的接收连接进行监听。监听套接字的功能包含所有的基本套接字的功能。 
 
 
Datagram Sockets: 
数据报套接字被用来发送和接收数据报。数据报套接字包含基本套接字的所有功能。 
 
 
Connection-Oriented Sockets: 
面向连接的套接字被用来在建立的连接上发送和接收网络数据。面向连接的套接字包含所有基本套接字的功能。 
 
 
Winsock Kernel Events 
当有一些指定套接字的事件发生的时候,比如新的数据已经被套接字接收,或套接字被断开连接,WSK子系统可以异步通知WSK应用。为了WSK可以在套接字事件发生被异步通知到,WSK必须执行一些相应的事件回调函数,并在套接字上使能这些回调函数。 
 
注意:WSK应用不需要执行或使用事件回调函数。WSK应用可以通过调用相应的WSK套接字函数来实现大多数的WSK套接字操作。WSK应用唯一需要事件回调函数,是在监听套接字的有条件接收模式上的回调函数上。 
 
不同的WSK套接字类型支持不同的套接字事件。 
 
 
Basic sockets: 
基本套接字不支持任何套接字事件。 
 
Listening sockets: 
Event                                                                     Event callback function  
An incoming connection has been accepted.                  WskAcceptEvent  
An incoming connection request has arrived.*                WskInspectEvent  
An incoming connection request has been dropped.*      WskAbortEvent  
 
* Applies only to listening sockets that have conditional-accept mode enabled 
 
Datagram sockets 
Event                                                                       Event callback function  
One or more new datagrams have been received.           WskReceiveFromEvent  
 
Connection-oriented sockets 
 
Event                                                          Event callback function  
New data has been received.                             WskReceiveEvent  
The socket has been disconnected.                     WskDisconnectEvent  
The ideal send backlog size has changed.            WskSendBacklogEvent  
 
当WSK应用创建一个套接字,套接字的事件回调函数默认是关闭的。当套接字事件发生的时候,为了WSK子系统去调用套接字的事件回调函数,WSK应该去使能套接字回调函数。 
 
如果WSK应用为套接字注册了额外的扩展接口,扩展接口可能支持额外的事件。 
 
WSK子系统也会通知WSK应用一些套接字没有指定的事件。为了WSK应用在这些事件上得到通知,WSK应用必须实现WskClientEvent回调函数。没有对特殊的套接字指定事件。WSK应用的WskClientEvent事件回调函数一直是使能的,不能被关闭。 
 
WSK应用的事件回调函数中禁止等待WSK完成上下文中的其他WSK请求完成,或其他的事件回调函数的完成。回调函数可以初始化其他的WSK请求(假设在DISPATCH_LEVEL上不会消耗太多时间),但是不能等待他们完成当回调函数在IRQL=PASSIVE_LEVEL上被调用的时候。 
 
 
Using Winsock Kernel Functions vs. Event Callback Functions 
对于一些套接字的操作,WSK应用可调用套接字的WSK函数去执行潮州,或者去使能套接字上面的事件回调函数,在套接字相关事件发生时,WSK子系统调用事件回调函数去完成操作。举例来说,当在一个面向连接的套接字上接收数据,WSK应用可以调用WskReceive函数,或者去使能套接字上的WskReceiveEvent事件回调函数。 
 
如下说明了关于每种方法的关键点。 
 
Using Winsock Kernel Functions 
WSK应用控制套接字的操作,意味着WSK应用控制套接字的操作什么时候发生。这个可以通过WSK应用进行简单的同步。 
WSK应用提供IRP给套接字函数。WSK子系统对这些IRP进行排队知道套接字的操作完成。 
WSK应用通过等待每个操作的IRP被WSK子系统完成执行一些阻塞套接字操作。 
 
为了避免接收的数据报来自一个正在数据报套接字上被丢弃,或避免接收的连接正在监听套接字上正在被丢弃,WSK应用为了确保在面向连接的套接字上保持高的数据传输效率,在一定的条件下,必须保持在队列中对多个套接字操作。 
 
WSK应用为数据传输操作提供了数据空间。这个可以降低数据被拷贝的次数。 
如果,WSK需要在一个队列中保持多个数据传输操作,应用必须为每一个数据传输操作提供一个数据空间,这样,WSK应用需要额外的内存资源。 
 
Using Event Callback Functions 
WSK子系统控制套接字操作,意味着WSK子系统通过调用套接字事件的回调函数通知WSK应用的套接字事件。WSK应用可能需要更复杂的同步机制去处理事件回调函数的异步操作。 
 
WSK应用对套接字操作没有使用IRP. 
 
WSK应用不需要排队套接字操作,WSK子系统在套接字事件发生时很快调用WSK应用事件回调函数。如果WSK应用可以在其套接字的回调函数被调用时,保持很高速率,使用事件回调函数可以提供最高的表现,和最少的丢掉数据报或接收连接的机会。 
 
WSK子系统为数据传输操作提供数据空间,WSK应用必须立刻,或有原因的有限时间内,释放这些数据空间给WSK子系统,以至于,WSK子项题哦那个不会运行时没有内存资源。那样的话,WSK应用可能需要拷贝数据从WSK子系统提供的空间到自己的数据空间中。 
 
Winsock Kernel Dispatch Tables 
 
WSK的套接字对象包含一个指向其函数派遣函数列表的指针。WSK应用调用派遣函数列表中的函数去执行套接字上的网络IO操作。因为,每一种WSK套接字的分类的不同,其派遣函数列表也不同。WSK NPI对于每一类套接字定义了不同的派遣函数列表: 
 
Socket category                         Dispatch table structure 
Basic socket                              WSK_PROVIDER_BASIC_DISPATCH 
Listening socket                         WSK_PROVIDER_LISTEN_DISPATCH 
Datagram socket                       WSK_PROVIDER_DATAGRAM_DISPATCH 
Connection-oriented socket         WSK_PROVIDER_CONNECTION_DISPATCH  
 
如果WSK应用对于套接字使用的事件回调函数是其自己创建的,它就比西在创建一个新的套接字的时候,提供一个好套接事件回调函数指针的客户派遣函数列表结构。因为,每一类套接字支持不同的事件回调函数,WSK NPI为每一类的套接字定义不同的客户派遣函数列表。 
 
Socket category                         Dispatch table structure 
Listening socket                         WSK_CLIENT_LISTEN_DISPATCH 
Datagram socket                        WSK_CLIENT_DATAGRAM_DISPATCH 
Connection-oriented socket         WSK_CLIENT_CONNECTION_DISPATCH  
 
Winsock Kernel Extension Interfaces 
WSK NPI包含一些扩展接口的支持。WSK子系统可以在WSK套接字一组套接字函数和事件回调函数的基础上使用扩展接口扩展其功能。每一个NPI定义的扩展接口都是和WSK NPI定义的。当前没有扩展被定义。 
 
WSK应用可以通过使用SIO_WSK_REGISTER_EXTENSION套接字IOCTL操作注册WSK子系统提供的扩展接口。WSK应用可以注册对于套接字对套接字的基础的扩展接口。 
 
 
Using IRPs with Winsock Kernel Functions 
WSK NPI使用IRP进行网络IO操作的异步完成操作。每一个WSK函数带一个指向IRP的指针作为参数。WSK子系统在WSK函数的操作完成以后结束IRP. 
WSK应用使用的传给WSK函数的IRP,可以起源于如下的方式: 
WSK应用通过调用IoAllocateIrp函数分配IRP.这种情况下,WSK应用必须分配IRP,至少有一个IO堆栈。 
WSK重用预前分配的已经完成的IRP.在这种情况下,WSK必须调用IoReuseIrp函数去重新初始化IRP. 
WSK应用使用的从更高层驱动或IO管理器传递过来的IRP.在这种情况下,IRP必须至少有一个保留的IO堆栈给WSK子系统使用。 
 
当WSK应用拥有一个IRP来调用WSK函数以后,它可以为IRP设置一个IoCompletion完成例程,当IRP被WSK子系统完成的时候,完成例程被调用。这个可以通过IoSetCompletionRountine函数来实现。取决于IRP怎样初始化,IoCompletion例程可以必须的或可选的。 
 
如果WSK应用分配一个IRP,或重用一个预前分配的IRP,它可以在调用WSK函数前,为IRP设置一个完成例程。在这种情况下,WSP应用必须指定IoSetCompletionRoutine函数的参数InvokeOnSuccess,InvokeOnError InvokeOnCancel都设置为TRUE,确保完成例程总是被调用。而且,对于被设置了完成例程的IRP必须总是返回STATUS_MORE_PROCESSING_REQUIRED去临时结束IRP的处理。如果WSK应用操作已经结束,IRP的完成例程被调用,在完成例程返回前必须调用IoFreeIrp去释放IRP。如果WSK应用没有释放IRP,它就可以重用它去调用另外的WSK函数。 
 
如果WSK应用使用的IRP是从上层驱动传递过来的或IO管理器传递过来的,仅仅是在WSK函数被完成的时候它必须被通知的情况下,才在调用WSK函数前,为IRP设置IoCompletion完成例程。如果WSK应用没有为IRP设置完成例程,当IRP被完成的时候,IRP将被传递到更高层驱动或IO管理器进行一般IRP完成处理。如果,WSK应用为IRP设置了IoCompletion例程,IoCompletion例程可以返回STAUTS_SUCCESS或STATUS_MORE_PROCESSING_REQUIRED.如果返回STATUS_SUCCESS,IRP完成处理安装普通的方式处理,如果返STATUS_MORE_PROCESSING_REQUIRED,WSK应用在其WSK函数已经完成处理并返回结果后,调用IoCompleteRequest结束IRP.WSK应用不 
应该释放从更高层驱动或IO管理器传递过来的IRP. 
 
如果WSK应用为从更高层驱动或IO管理器传递过来的IRP设置IoCompletion例程的时候,IoCompletion例程必须检查IRP的成员PendingReturned,在其为TRUE的时候,调用IoMarkIrpPending函数。 
 
WSK应用,除了为IRP设置完成例程IoCompletion外,不应该为传递给WSK函数的IRP做其他的初始化动作。当WSK应用传递IRP到一个WSK函数,WSK根据应用的行为为其安装一个下层IO堆栈。 
 
如下的代码演示了在套接字上执行接收操作的时候WSK应用怎样分配和使用IRP. 
 
- // Prototype for the receive IoCompletion routine
 
 - NTSTATUS
 
 - ReceiveComplete(
 
 - PDEVICE_OBJECT DeviceObject,
 
 - PIRP Irp,
 
 - PVOID Context
 
 - );
 
  
 
- // Function to receive data
 
 - NTSTATUS
 
 - ReceiveData(
 
 - PWSK_SOCKET Socket,
 
 - PWSK_BUF DataBuffer
 
 - )
 
 - {
 
 - PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
 
 - PIRP Irp;
 
 - NTSTATUS Status;
 
  
 
- // Get pointer to the 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,
 
 - ReceiveComplete,
 
 - DataBuffer,  // Use the data buffer for the context
 
 - TRUE,
 
 - TRUE,
 
 - TRUE
 
 - );
 
  
 
- // Initiate the receive operation on the socket
 
 - Status =
 
 - Dispatch->WskReceive(
 
 - Socket,
 
 - DataBuffer,
 
 - 0,  // No flags are specified
 
 - Irp
 
 - );
 
  
 
- // Return the status of the call to WskReceive()
 
 - return Status;
 
 - }
 
  
 
- // Receive IoCompletion routine
 
 - NTSTATUS
 
 - ReceiveComplete(
 
 - PDEVICE_OBJECT DeviceObject,
 
 - PIRP Irp,
 
 - PVOID Context
 
 - )
 
 - {
 
 - UNREFERENCED_PARAMETER(DeviceObject);
 
  
 
- PWSK_BUF DataBuffer;
 
 - ULONG ByteCount;
 
  
 
- // Check the result of the receive operation
 
 - if (Irp->IoStatus.Status == STATUS_SUCCESS)
 
 - {
 
 - // Get the pointer to the data buffer
 
 - DataBuffer = (PWSK_BUF)Context;
 
  
- // Get the number of bytes received
 
 - ByteCount = (ULONG)(Irp->IoStatus.Information);
 
  
 
- // Process the received data
 
 - ...
 
 - }
 
  
 
- // 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;
 
 - }
 
  
- 如下的代码演示了WSK应用在套接字上执行接收操作时如何使用从更高层驱动传递过来的或IO管理器传递过来的IRP。
 
  
 
- // Prototype for the receive IoCompletion routine
 
 - NTSTATUS
 
 - ReceiveComplete(
 
 - PDEVICE_OBJECT DeviceObject,
 
 - PIRP Irp,
 
 - PVOID Context
 
 - );
 
  
 
- // Function to receive data
 
 - NTSTATUS
 
 - ReceiveData(
 
 - PWSK_SOCKET Socket,
 
 - PWSK_BUF DataBuffer,
 
 - PIRP Irp;  // IRP from a higher level driver or the I/O manager
 
 - )
 
 - {
 
 - PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
 
 - NTSTATUS Status;
 
  
 
- // Get pointer to the provider dispatch structure
 
 - Dispatch =
 
 - (PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
 
  
 
- // Set the completion routine for the IRP such that it is
 
 - // only called if the receive operation succeeds.
 
 - IoSetCompletionRoutine(
 
 - Irp,
 
 - ReceiveComplete,
 
 - DataBuffer,  // Use the data buffer for the context
 
 - TRUE,
 
 - FALSE,
 
 - FALSE
 
 - );
 
  
 
- // Initiate the receive operation on the socket
 
 - Status =
 
 - Dispatch->WskReceive(
 
 - Socket,
 
 - DataBuffer,
 
 - 0,  // No flags are specified
 
 - Irp
 
 - );
 
  
 
- // Return the status of the call to WskReceive()
 
 - return Status;
 
 - }
 
  
 
- // Receive IoCompletion routine
 
 - NTSTATUS
 
 - ReceiveComplete(
 
 - PDEVICE_OBJECT DeviceObject,
 
 - PIRP Irp,
 
 - PVOID Context
 
 - )
 
 - {
 
 - UNREFERENCED_PARAMETER(DeviceObject);
 
  
 
- PWSK_BUF DataBuffer;
 
 - ULONG ByteCount;
 
  
 
- // Since the completion routine was only specified to
 
 - // be called if the operation succeeds, this should
 
 - // always be true.
 
 - ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);
 
  
 
- // Check the pending status of the IRP
 
 - if (Irp->PendingReturned == TRUE)
 
 - {
 
 - // Mark the IRP as pending
 
 - IoMarkIrpPending(Irp);
 
 - }
 
  
 
- // Get the pointer to the data buffer
 
 - DataBuffer = (PWSK_BUF)Context;
 
  
- // Get the number of bytes received
 
 - ByteCount = (ULONG)(Irp->IoStatus.Information);
 
  
 
- // Process the received data
 
 - ...
 
  
 
- // Return STATUS_SUCCESS to continue the
 
 - // completion processing of the IRP.
 
 - return STATUS_SUCCESS;
 
 - }
 
 
  复制代码 |   
 
 
 
 |