?第二部分Winsock API
?
?Wi n s o c k是網絡編程接口,而不是協議。它從U n i x平臺的B e r k e l e y(B S D)套接字方案借鑒了許多東西,后者能訪問多種網絡協議。在Wi n 3 2環境中,Wi n s o c k接口最終成為一個真正的“與協議無關”接口,尤其是在Winsock 2發布之后。
本部分開始之前,我們假定大家已具備了Wi n s o c k(或B S D套接字)的基本知識,而且多少熟悉一些客戶機/服務器的Wi n s o c k基本知識。
第5章網絡原理和協議
建立Winsock 2規范的主要目的是提供一個與協議無關的傳送接口。
5.1 協議的特征
本章第一小節著重講解目前常用網絡傳送協議的一些基本特征。通過這里的學習,大家可掌握與協議行為類型有關的一些背景知識。同時,作為程序員,可大致知道特定協議在程序中的行為方式。
5.1.1 面向消息
對每個離散寫命令來說,如果傳送協議把它們(而且只有它們)當做一條獨立的消息在網上傳送,我們就說該協議是面向協議的。同時,還意味著接收端在接收數據時,返回的數據是發送端寫入的一條離散消息。接收端不能得到更多的消息,僅此而已。比如,在圖5 - 1中,
左邊的工作站向右邊的工作站提交了三條分別是1 2 8、6 4和3 2字節的消息。作為接收端的工作站發出三條讀取命令,緩沖區是2 5 6個字節。后來的各次調用返回的分別是1 2 8、 6 4 和3 2個字節。第一次讀取調用不會將這所有的三個數據包都返回,即使這些數據包已經收到也如此。這稱為“保護消息邊界”(preserving message boundaries),,一般出現在交換結構化數據時。
網絡游戲是“保護消息邊界”的較好范例。每個玩家均向別的玩家發出一個帶有地圖信息的數據包。這種通信后面的代碼很簡單:一個玩家請求一個數據包,另一個玩家又準確地從別的玩家處獲得一個地圖信息數據包。
無保護消息邊界的協議通常稱作“基于流的協議”。大家要知道“基于流的協議”這一術語常用來指代附加特性。流服務的定義是連續的數據傳輸;不管消息邊界是否存在,接收端都會盡量地讀取有效數據。對發送端來說,意味著允許系統將原始消息分解成小消息或把幾條消息積累在一起,形成一個較大的數據包。對接收端來說,則是數據一到達網絡堆棧,
網絡堆棧就開始讀取它,并將它緩存下來等候進程處理。在進程請求處理大量數據時,系統會在不溢出為客戶請求提供的緩沖區這一前提下,盡量返回更多的數據。在圖5 - 2中,發送端提交了三個數據包:分別是1 2 8、6 4和3 2個字節;但是,本地系統堆棧自由地把這些數據聚合在一起,形成一個大的數據包。這種情況下,重組后的2個數據包就會一起傳輸。是否將各個獨立的數據包累積在一起受許多因素的影響,比如最大傳輸單元或N a g l e算法。在T C P / I P中,在將積累起來的數據發到線上之前, N a g l e算法在等候數據積累的主機中進行。
在需要發送的數據積累到一定數量或預定時間已超過之前,這個主機會一直等下去。實施N a g l e算法時,主機的通信方在發送主機確認之前,會等一等外出數據,主機的通信方就不必發送一個只有確認的數據包。發送小數據包不僅沒有多少意義,而且還會徒增錯誤檢查和確認,相當煩人。
在接收端,網絡堆棧把所有進來的數據包聚集在一起,歸入既定進程。我們來看看圖5 - 2。
如果接收端執行一次2 5 6字節緩沖區的讀取,系統馬上就會返回2 2 4個字節。如果接收端只要求讀取2 0個字節,系統就會只返回2 0個字節。
偽流
偽流( p s e u d o - s t r e a m)這個術語常用于某種系統中,該系統使用的協議是基于消息的,
發送的數據分別在各自獨立的數據包內,接收端讀取并把消息緩存在一起, 這樣,接收應用
程序便可讀取任意大小的數據塊。把圖5 - 1中的發送端和圖5 - 2中的接收端結合起來,便可說明
偽流的工作原理。發送端必須分別發送各自獨立的數據包,但接收端可以對收到的數據包自
由組合。一般情況下,可把偽流視作一個普通的“面向流的協議”。
5.1.2 面向連接和無連接
通常情況下,一個協議提供面向連接的服務,或提供無連接的服務。面向連接的服務中,
進行數據交換之前,必須與通信方建立一條路徑。這樣既確定了通信方之間存在路由,又保證了通信雙方都是活動的、都可彼此響應,但其特點是在通信雙方之間建立一個通信信道需要很多開支。除此以外,大部分面向連接的協議為保證投遞無誤,可能會因為執行額外的計算來驗證正確性,因此,進一步增加開支。而無連接協議卻不保證接收端是否正在收聽。無連接服務類似于郵政服務:發信人把信裝入郵箱即可。至于收信人是否想收到這封信或郵局是否會因為暴風雨未能按時將信件投遞到收信人處等等,發信人都不得而知。
5.1.3 可靠性和次序性
在設計用于特定協議的應用程序來說,可靠性和次序性是我們必須了解的最具決定性的特性。大多數情況下,可靠性和次序性與協議是無連接的,還是面向連接的密切相關。
5.1.4 從容關閉
從容關閉只出現在面向連接的協議中。在這種關閉過程中,一方開始關閉通信會話,但另一方仍然可以讀取線上或網絡堆棧上已掛起的數據。如果面向連接的協議不支持從容關閉,
只要其中一方關閉了通信信道,都會導致連接立即中斷,數據丟失,接收端不能讀取數據這些情況出現。
5.1.5 廣播數據
廣播數據即數據從一個工作站發出,局域網內的其他所有工作站都能收到它。這一特征
適用于無連接協議,因為L A N上的所有機器都可獲得并處理廣播消息。。一般情況下,路由器都不會傳送廣播包。 ?
5.1.6 多播數據
多播是指一個進程發送數據的能力,這些數據即將由一個或多個接收端進行接收。進程加入一個多播會話的方法和采用的基層協議有關。視頻會議應用常常使用多播。
5.1.7 服務質量
服務質量( Q o S)是應用的一種能力,用以請求針對專門用途分配特定的帶寬。
5.1.8 部分消息
部分消息只用于面向消息的協議。
5.1.9 路由選擇的考慮
一個重要考慮就是協議是否可路由。如果協議可路由,就可在兩個工作站之間建立一條成功的通信路徑(要么是面向連接的回路,要么是數據報的數據路徑),不管這兩個工作站之間存在的網絡硬件是什么。路由器不對發自非路由協議的數據包進行轉發,即便數據包的既定目的地在其連接的子網上。
5.2 支持的協議
Wi n 3 2平臺提供的最有用的特征之一是能夠同步支持多種不同的網絡協議。
利用Wi n s o c k編程接口的好
處之一是因為它是一個與協議無關的接口。不管使用的是哪一種協議,它們的操作大多數是
相通的。
要想獲得系統中安裝的網絡協議的相關信息,調用這個函數W S A E n u m P r o t o c o l s即可,
打開Winsock在可以調用一個Wi n s o c k函數之前,必須先加載一個版本正確的Wi n s o c k庫。Wi n s o c k
啟動例程是W S A S t a r t u p,它的定義是:
int WSAStartup(WORD wVe r s i o n R e q u e s t e d , L P W S A D ATA lpWSAData)
第一個參數是準備加載的Wi n s o c k庫的版本號。就目前的Wi n 3 2平臺而言,Winsock 2
庫的最新版本是2 . 2。唯一的例外是Windows CE,它只支持Winsock 1.1版。如果需要
Winsock 2.2版,指定這個值( 0 x 0 2 0 2)或使用宏M A K E W O R D ( 2 , 2 )即可。高位字節指定
副版本,而低位字節則指定主版本。
第二個參數是W S A D ATA結構,它是調用完成之后立即返回的。W S A D ATA包含了W S A S t a r t u p加載的關于Wi n s o c k版本的信息。
大致說來,在W S A D ATA結構中,返回的唯一有用的信息是w Ve r s i o n和w H i g h Ve r s i o n。
屬于最大套接字和最大U D P長度的條目應該從自己正在使用的特定協議目錄條目中獲取。
在結束Wi n s o c k庫,而且不再需要調用任何Wi n s o c k函數時,會卸載這個庫,并釋放資源。這個函數的定義是:
int WSACleanup (void);
記住,每次調用W S A S t a r t u p,都需要調用相應的W S A C l e a n u p,因為每次啟動調用都
會增加對加載Winsock DLL的引用次數,它要求調用同樣多次的W S A C l e a n u p,以此抵消
引用次數。
5.4 Windows套接字
。所謂套接字,就是一個指向傳輸提
供者的句柄。Wi n 3 2中,套接字不同于文件描述符,所以它是一個獨立的類型—S O C K E T。
套接字是由兩個函數建立的:
SOCKET WSASocket(int af,
???????? int type,
???????? int protocol,
???????? LPWSAPROTOCOL_INOF lpProtocolInfo,
???????? GROUP g,
???????? DWORD dwFlag
???????? );
SOCKET socket(int af,
???????int type,
???????int protocol
???????);
第一個參數a f,是協議的地址家族。比如,如果想建立一個U D P或T C P套接字,可用常量A F _ I N E T來指代互聯網協議( I P)。
第二個參數t y p e,是協議的套接字類型。套接字的類型可以是下面五個值:
?S O C K _ S T R E A M、S O C K _ D G R A M、S O C K _ S E Q PA C K E T、S O C K _ R AW和S O C K _ R D M。
第三個參數是p r o t o c o l。指定的地址家族和套接字類型有多個條目時,就可用
這個字段來限定使用特定傳輸。
最后兩個W S A S o c k e t標志很簡單。組參數始終為0,因為目前尚無可支持套接字組的
Wi n s o c k版本。要指定一個或多個下列標志,可用d w F l a g s參數:
■ W S A _ F L A G _ O V E R L A P P E D
■ W S A _ F L A G _ M U LT I P O I N T _ C _ R O O T
■ W S A _ F L A G _ M U LT I P O I N T _ C _ L E A F
■ W S A _ F L A G _ M U LT I P O I N T _ D _ R O O F
■ W S A _ F L A G _ M U LT I P O I N T _ D _ L E A F
第一個標志W S A _ F L A G _ O V E R L A P P E D,用于指定這個套接字具備重疊I / O(是適用于
Wi n s o c k的可能實現的通信模式之一)。這個主題將在第8章詳細討論。調用s o c k e t建立一個套
接字時, W S A _ F L A G _ O V E R L A P P E D便是默認設置。一般說來,在使用W S A S o c k e t時,最好
始終保持設定該標志。后面四個標志用于處理多播套接字。
原始套接字
利用W S A S o c k e t建立套接字時,可向函數調用傳送一個W S A P R O TO CO L _ I N F O結構,以
定義準備建立的那個套接字的類型;盡管如此,還是可建立一些套接字類型(在傳輸提供者
目錄中,它們沒有相應的條目)。最佳示例是I P協議下的原始套接字。原始套接字一種通信,
允許你把其他協議封裝在U D P數據包中,比如說“互聯網控制消息協議”(I C M P)。I C M P的
目的是投遞互聯網主機間的控制、錯誤和信息型消息。由于I C M P不提供任何數據傳輸功能,
因此不能把它與U D P或T C P同等看待,但它和I P本身屬于同一個級別。
Winsock API安裝在“會話層”和“傳送層”之間。
5.6 選擇適當的協議
T C P / I P就是首選協議之一,至少從支持能力和微軟的贊助這一角度來看,是
這樣的
5.7 小結
通過本章的學習,大家已了解為應用程序選擇網絡傳輸時應該知道的基本特性。在為指100計計第二部分附Winsock API
下載定的協議開發成功的網絡應用程序時,了解這些特性是至關重要的。我們還有計劃地深入探討了如何獲得安裝在系統中的傳輸提供者列表和如何查詢特定屬性。最后,我們還學習了如何為指定的傳輸建立套接字,方法是為W S A S o c k e t或s o c k e t函數指定正確的參數,再利用
W S A E n u m P r o t o c o l s查詢目錄條目以及通過W S A P R O TO C O L _ I N F O結構,把函數投遞到W S A S o c k e t。下一章,我們將進一步探討各主要協議的定址方法。
?