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,用於取得調用出錯后的出錯代碼。