以下部分取自网络,部分取自书籍,部分是自己理解,均可能有误,如有误导,多有得罪。
SPI和WinSock
Winsock SPI全称是The Winsock Service Provider Interface
。是提供网络服务的基本模块所调用的一些api,而winsock则基于SPI定义一应用程序编程接口(给普通应用程序访问和控制网络而使用的api接口)
LSP分两种:一种是IFS LSP,一种是non IFS LSP。
简单地说, IFS LSP制作简单,可以完成大部分的数据包监听工作;
non IFS LSP制作复杂,但是可以进行一些特殊的overlapped I/O操作,如在overlapped初始化完成后、调用WSPSend (WriteFile)、 WSPSendTo、WSPRecv (ReadFile)、 WSPRecvFrom,、or WSPIoctl之前对数据进行一些处理工作。
LSP相互之间可以叠加,但在non IFS LSP之上不可以叠加IFS LSP。也就是说,如果一个BSP是non IFS,则第三方提供的LSP必须是non IFS,否则无法安装在SPI上。
分层协议
传输服务提供者有两类:基础服务提供者(BSP)和分层服务提供者(即LSP)。基础服务提供者执行网络传输协议(比如TCP/IP)的具体细节其中包括在网络上收发数据之类的核心网络协议功能。“分层式”(Layered)服务提供者只负责执行高级的自定义通信功能,并依靠下面基础服务提供者,在网络上进行真正的数据交换。
而服务提供者的协议信息里(WSAPROTOCOL_INFO结构的ProtocolChain)表示了是否该依赖于其它分层或基本协议。
- 如果此成员长度为1,则表示它是一个基本协议;
- 如果此成员长度为0,则表示是一个分层协议;
- 并且如果长度大于1,则表示它是一个协议链,则一个或多个基本协议之上的分层协议组成(ProtocolChain.ChainEntries包含了这些协议的标识符)。
一般我们开发的LSP程序都是分层协议,基于原有SPI协议做一些事情。
IFS
from : https://docs.microsoft.com/zh-cn/windows/win32/winsock/socket-handles-2
A socket handle can optionally be a file handle in Windows Sockets 2. A socket handle from a Winsock provider can be used with other non-Winsock functions such as ReadFile, WriteFile, ReadFileEx, and WriteFileEx.
The XP1_IFS_HANDLES member in the protocol information structure for a provider determines whether a socket handle from a provider is an Installable File System (IFS) handle. Socket handles that are IFS handles can be used without a performance penalty with other non-Winsock functions (ReadFile and WriteFile, for example).
Any non-IFS socket handles when used with non-Winsock functions (ReadFile and WriteFile, for example) result in interactions between the provider and the file system where extra processing overhead is involved that can result in a significant performance penalty. When using socket handles with non-Winsock functions, the error codes propagated from the base file system are not always mapped to Winsock error codes. Consequently, it is recommended that socket handles be used only with Winsock functions.
反正意思就是,如果provider的协议里(WSAPROTOCOL_INFO结构的dwServiceFlags1成员)有XP1_IFS_HANDLES标志,则这个socket就可以直接使用ReadFile等文件函数,并且几乎无性能损耗(多少还是有一些)。
当SPI客户机调用WSPSocket、WSPAccept和WSPJointLeaf函数时,服务提供者必须返回套接字句柄。返回SPI客户机的套接字句柄既可以是可安装文件系统(IFS)句柄,又可以是非IFS句柄。如果一个服务提供者返回的是IFS句柄,就会被视作IFS提供者;反之,就是非IFS提供者。微软的基础传输提供者全都是IFS句柄。
如果你是一个IFS提供者,则你将一个socket返回给调用者时就会导致以下限制:
- 如果在一个套接字上调用了ReadFile和WriteFile,便不会调用分层提供者的WSPSend
和WSPRecv函数。这些函数将绕过分层提供者,直接调用基础IFS提供者的实施。 - 分层提供者将不能后处理提交到一个完成端口的重叠I/O请求。完成端口的后处理完成
绕过了分层式提供者。
LSP相关函数解析
WSPStartup
每一个LSP模块都要提供的一个入口函数,定义为:
int WSPStartup(
WORD wVersionRequested,
OUT LPWSPDATA lpWSPData,
LPWSAPROTOCOL_INFOW lpProtocolInfo,
WSPUPCALLTABLE UpcallTable,
OUT LPWSPPROC_TABLE lpProcTable
);
其中lpWSPData和lpProcTable都是我们编写的LSP模块要提供给调用者的,而lpProtocolInfo和UpcallTable是调用者提供给LSP模块使用的。lpProtocolInfo指向的WSAPROTOCOLINFOW结构中包含一个字段ProtocolChain,该字段标志你的服务提供者和机器上的其他提供者是如何排序的。分层式服务提供者的一项要求是搜索ProtocolChain字段,以便决定它自己在服务提供者数组中所处的位置(通过搜索层目录条目这一方式),以及确定数组中的下一个提供者。如果下一个提供者是另一层,就必须把未经修改的lpProtocolInfo结构投给下一层的WSPStartup函数。如果下一层提供者是本数组中的最后一位(即基础提供者),你的提供者就必须在调用基础提供者的WSPStartup函数之前,利用这个基础提供者的WSAPROTOCOLINFOW结构,在lpProtocolInfo结构上执行交换。程序清单14-1演示了分层提供者应该怎样通过编程来管理lpProtocolInfo结构。
- lpProtocolInfo 调用者传递来的协议特征,有时一个LSP的dll可以注册多种SPI,通过此字段能识别并匹配是哪种SPI,然后提供不同的服务。
- lpProcTable 输出参数。是一个WSPxxx函数数组,提供给上层调用者使用。常规的WSA函数,比如WSASocket都可以映射为对应的WSP函数,winsock在用户调用WSASocket时,就会来加载各种LSP模块(我们写的LSP需要加载下层LSP或BSP),然后LSP如果需要修改这个函数的执行,就将自己实现的WSP函数指针替换下层SPI返回的指针数组。
- UpcallTable 是输入参数,是提供给SPI程序中调用winsock相关代码用的。
你的服务提供者就应该保留一个实例计数,以便了解WSPStartup函数被调用了多少次,再相应地调用WSPCleanup多少次,以便抵消WSPStartup被调用的次数。
SPI安装
传输提供者的安装方式决定了它是一个分层提供者(LSP),还是一个基础提供者(BSP)。安装程序只在Winsock2系统配置数据库中,配置了传输提供者,这个系统配置数据库是一个目录,系统上已安装的服务提供者都在这个目录中。配置数据库让Winsock2得知服务提供者已存在,并定义了提供的服务类型。Winsock应用程序建立套接字时,Winsock2便利用这个数据库来判断需要加载哪些传输服务提供者。WS2_32.DLL在数据库中搜索第一个与socket和WSASocketAPI调用的套接字输入参数(比如说地址家族、套接字类型和协议)匹配的提供者。一旦找到与之匹配的条目,Ws2_32.dll便加载相应的服务提供者之DLL(动态数据链接库),这个DLL是定义在该目录中的。
安装卸载和查询它们需要四个SPI函数。这四个函数的前缀均为WSC:
- WSCEnumProtocols
- WSCInstallProvider
- WSCWriteProviderOrder
- WSCDeInstallProvider
这些函数利用WSAPROTOCOL_INFOW结构,对服务提供者数据库进行查询和操作。
网友评论