天天看點

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——8.8 驗證接收到的響應

本節書摘來自異步社群《unix網絡程式設計 卷1:套接字聯網api(第3版)》一書中的第8章,第8.8節,作者:【美】w. richard stevens , bill fenner , andrew m. rudoff著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

在8.6節結尾我們提到,知道客戶臨時端口号的任何程序都可往客戶發送資料報,而且這些資料報會與正常的伺服器應答混雜。我們的解決辦法是修改圖8-8中的recvfrom調用以傳回資料報發送者的ip位址和端口号,保留來自資料報所發往伺服器的應答,而忽略任何其他資料報。然而這樣做照樣存在一些缺陷,我們馬上就會看到。

我們首先把客戶程式的main函數(圖8-7)改為使用标準回射伺服器(圖2-13)。這隻需把以下指派語句

<code>servaddr.sin_port = htons(serv_port);</code>

替換為

<code>servaddr.sin_port = htons(7);</code>

這樣,我們的客戶就可以使用任何運作标準回射伺服器的主機了。

我們接着重寫dg_cli函數以配置設定另一個套接字位址結構用于存放由recvfrom傳回的結構,如圖8-9所示。

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——8.8 驗證接收到的響應

配置設定另一個套接字位址結構

9 我們調用malloc來配置設定另一個套接字位址結構。注意dg_cli函數仍然是協定無關的,因為我們并不關心所處理套接字位址結構的類型,而隻是在malloc調用中使用其大小。

比較傳回的位址

12~18 在recvfrom的調用中,我們通知核心傳回資料報發送者的位址。我們首先比較由recvfrom在值-結果參數中傳回的長度,然後用memcmp比較套接字位址結構本身。

我們在3.2節說過,即使套接字位址結構包含一個長度字段,我們也不必設定或檢查它。然而此處memcmp比較兩個套接字位址結構中的每個資料位元組,而核心傳回套接字位址結構時,其中長度字段是設定的;是以對于本例,與之比較的另一個套接字位址結構也必須預先設定其長度字段。否則,memcmp将比較一個值為0的位元組(因為沒有設定長度字段)和一個值為16的位元組(假設具體為sockaddr_in結構),結果自然不比對。

如果伺服器運作在一個隻有單個ip位址的主機上,那麼這個新版本的客戶工作正常。然而如果伺服器主機是多宿的,該客戶就有可能失敗。我們針對有兩個接口和兩個ip位址的主機freebsd4運作本客戶程式。

我們指定的伺服器ip位址不與客戶主機共享同一個子網。

這樣指定伺服器ip位址通常是允許的。①大多數ip實作接受目的位址為本主機任一ip位址的資料報,而不管資料報到達的接口(tcpv2第217~219頁)。rfc 1122[braden 1989]稱之為弱端系統模型(weak end system model)。如果一個系統實作的是該rfc中所說的強端系統模型(strong end system model),那麼它将隻接受到達接口與目的位址一緻的資料報。

recvfrom傳回的ip位址(udp資料報的源ip位址)不是我們所發送資料報的目的ip位址。當伺服器發送應答時,目的ip位址是172.24.37.78。主機freebsd4核心中的路由功能為之選擇172.24.37.94作為外出接口。既然伺服器沒有在其套接字上綁定一個實際的ip位址(伺服器綁定在其套接字上的是通配ip位址,這一點可通過在freebsd4上運作netstat來驗證),是以核心将為封裝這些應答的ip資料報選擇源位址。選為源位址的是外出接口的主ip位址(tcpv2第232~233頁)。還有,既然它是外出接口的主ip位址,如果我們指定發送資料報到該接口的某個非主ip位址(即一個ip别名),那麼也将導緻圖8-9版本客戶程式的測試失敗。

一個解決辦法是:得到由recvfrom傳回的ip位址後,客戶通過在dns(第11章)中查找伺服器主機的名字來驗證該主機的域名(而不是它的ip位址)。另一個解決辦法是:udp伺服器給伺服器主機上配置的每個ip位址建立一個套接字,用bind捆綁每個ip位址到各自的套接字,然後在所有這些套接字上使用select(等待其中任何一個變得可讀),再從可讀的套接字給出應答。既然用于給出應答的套接字上綁定的ip位址就是客戶請求的目的ip位址(否則該資料報不會被投遞到該套接字),這就保證應答的源位址與請求的目的位址相同。我們将在22.6節給出一個這樣的例子。

在多宿solaris系統上,伺服器應答的源ip位址就是客戶請求的目的ip位址。本節講述的情形針對源自berkeley的實作,這些實作基于外出接口選擇源ip位址。

繼續閱讀