SPI概述
- winsock2服务提供者接口(service provider interface)是Winsock API的补充。服务提供者接口,是应用程序使用的服务,而它本身不是应用程序,他的作用是向加载这个服务的应用导出自己。
- Winsock2符合windows开放服务体系(WINDOWS Open Service Architecture,WOSA)模式。此体系允许第三方服务提供者插入进去,而客户应用程序和Winsock2 DLL可以不用做任何改动。Winsock2的体系结构如下图所示,其中SPI由其中两个部分组成-传输服务提供者和命名空间服务提供者,它允许用户 开发这两种类型的服务提供者,每个部分提供的功能不同。
传输服务提供者(Transport Service Provider)
- 传输服务提供者,通常是指协议堆栈,是提供建立连接、传输数据、行使流控制、出错控制的服务。它由两种类型:分层的(Layered)和基础的(Base)
- 基础服务提供者(Base Service Provider缩写为BSP)负责实现传输协议的真正细节,他导出Winsock接口,此接口直接实现协议(如TCP/IP提供者)
- 分层协议提供者(Layered Service Provider缩写为LSP):将自己安装到Winsock目录中的基础提供者上面,很可能在其他分层提供者之间,截取来自应用程序的WinsockAPI调用。
- 分层服务提供者仅实现更高层的定制通信函数,它依靠现存的底层基础提供者来与远程终端做实际的数据交换。分层服务提供者位于基础服务提供者之上,依靠它实现各种功能,例如:可以在基础TCP/IP堆栈上实现安全管理器或者带宽管理器。只要在上层和下层边缘支持Winsock2SPI,就可以向他们中间链接提供程序。
命名空间提供者
- 命名空间提供者与传输服务相似,所不同的是它截获名称解析WinsockAPI调用。如gethostbyname和WSALookupServiceBegin。s命名空间提供者在命名空间目录安装自己,当应用程序执行名字解析时将会被调用。
- Winsock服务提供者API包含在WS2SPI.H文件中。共有四种类型的SPI函数,列出了每种函数类型的前缀,以及他们属于哪个提供者程序。
API前缀 | 描述 |
---|---|
WSC | 安装移除或者修改分层和名称空间提供者程序 |
WSP | 封层服务提供者API |
WPU | 封层服务提供者使用的支持函数 |
NSP | 命名空间提供者API |
- 基础传输提供者和命名空间提供者通常仅对操作系统开发商和传输堆栈商有效,使用分层传输服务提供者扩展基础传输服务提供者的功能是很有用的。之后使用的传输服务提供者,指的是传输服务提供者,它可以是分层传输服务提供者或者基础传输服务提供者。
Winsock协议目录
- LSP是系统组件,在开发LSP之前,先介绍现有的系统网络组件的结构。
- SPI提供三种协议:分层协议、基础协议、协议链。
- 分层协议在基础协议的上层,依靠底层基础协议实现更高级的通信服务。
- 基础协议是能够独立、安全的和远程端点实现数据通信的协议。
- 协议链是将一系列基础协议和分层协议按特定顺序连接在一起的链状结构。
- 系统上可用的不同协议包含在Winsock目录中。
协议特性
- Winsock用WSAPROTOCOL_INFO结构描述特定协议的完整信息,枚举协议便是 枚举一系列的WSAPROTOCOL_INFO结构,一个WSAPROTOCOL_INFO结构称为一个Winsock目录入口。如果协议拥有多种行为特性,每个不同的行为类型在系统里都会有不同的目录入口。
- 例如系统上安装了TCP/IP就会有两个IP入口:一个是TCP另一个是UDP
- 每个WSAPROTOCOL_INFO结构定义了一个提供者支持的协议、地址家族和套接字类型
typedef struct _WSAPROTOCOL_INFO{
DWORD dwServiceFlags1;//描述服务提供的服务的位掩码
DWORD dwServiceFlags2;
DWORD dwServiceFlags3;
DWORD dwServiceFlags4;
DWORD dwServiceFlags;//指定此协议在Winsock目录中的标识方式
GUID ProviderId;//服务提供者厂商安排的GUID
DWORD dwCatalogEntryId;//WS2_32.DLL为每个WSAPROTOCOL_INFO安排唯一的标识符
WSAPROTOCOLCHAIN ProtocolChain;//与此协议相关的WSAPROTOCOLCHAIN结构,说明了此协议在分层协议中所处的位置
int iVersion;//协议版本标识符
int iAddressFamily;//传递给socket的地址加载参数
int iMaxSockAddr;//地址最大长度,以字节为单位
int iMinSockAddr;//地址最小长度,以字节为单位
int iSocketType;//传递给socket函数的套接字类型参数
int iProtocol;//传递给socket函数的协议参数
int iProtocolMaxOffset;//添加到iProtocol的最大值
int iNetworkByteOrder;//是大尾顺序还是小尾
int iSecuityScheme;//安全方案
DWORD dwMessageSize;//此协议支持的最大消息长度,以字节为单位
/*
0代表这是一个基于流的协议如TCP,所以没有消息最大值这个概念
0x1表示发送消息的最大长度依赖下层网络的MTU。在套接字绑定之后,应用程序应该使用SO_MAX_MSG_SIZE套接字选项获取发送消息的最大长度
0xFFFFFFFF表示这个协议是基于消息的,但是对于发送的消息没有最大长度限制
*/
DWORD dwProviderReserved;//保留给服务提供者使用
DWORD szProtocol[WSAPROTOCOL_LEN+1];//标识此协议的可读字符串
}WSAPROTOCOL_INFO,*LPWSAPROTOCOL_INFO;
- WSAPROTOCOL_INFO结构中有两个重要的标识:ProviderId和dwCatalogEntryId。
- ProviderId是服务开发商提供的全局唯一标识,dwCatalogEntryId是WS2_32.DLL为每个WSAPROTOCOL_INFO结构安排的唯一标识,称为目录入口ID。
使用WinsockAPI函数枚举协议
- 当应用程序创建套接字时,套接字创建函数(SOCKET)便会使用WSAEnumProtocols函数枚举系统中安装的协议,根据传递的参数找一个与之匹配的协议,然后调用此协议的提供者导出的函数来完成这种Winsock调用,取得安装协议的函数调用是:
int WSAEnumProtocols(
LPINT lpiProtocols,//一个数组,如果为NULL,函数将返回所有协议
LPWSAPROTOCOL_INFO lpProtocolBuffer;//用来取得信息的缓冲区
LPDWORD lpdwBufferLength//输入上面缓冲区的长度,返回需要的长度
)
- 使用此函数的调用方法是调用两次,第一次使lpProtocolBuffer等于NULL,deBufferLength等于0.这个调用将会以WSAENOBUFS失败,但是lpdwBufferLength参数包含了所需的缓冲区常长度。
- 注:WSAEnumProtocols函数仅能枚举基础协议和协议链,不能枚举分层协议。
- WinsockSPI提供的枚举协议的函数是WSCEnumProtocols,它能够枚举各种协议,包括分层协议、基础协议和协议链。
- SPI是用于开发系统组件的函数,所以它使用的都是Unicode字符串,直接与windows系统相对应。WSAPROTOCOL_INFO的Unicode版是WSAPROTOCOL_INFOW,协议名称以Unicode字符串形式描述,其他没变化。下面是WSCEnumProtocols函数的定义。
int WSCEnumProtocols(LPINT lpiProtocols,LPWSAPROTOCOL_INFOW lpProtocolBuffer,LPDWORD lpdwBufferLength,LPINT lpErrno);
-
与WSAEnumProtocols相比,WSCEnumProtocols多了一个参数lpErrno,用于取得调用出错后的出错代码。