天天看點

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——8.9 伺服器程序未運作

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

我們下一個要檢查的情形是在不啟動伺服器的前提下啟動客戶。如果我們這麼做後在客戶上鍵入一行文本,那麼什麼也不發生。客戶永遠阻塞于它的recvfrom調用,等待一個永不出現的伺服器應答。然而這是一個很好的例子,它要求我們更多地了解底層協定以了解網絡應用程序将發生什麼。

首先,我們在主機macosx上啟動tcpdump,然後在同一個主機上啟動客戶,指定主機freebsd4為伺服器主機。接着,我們鍵入一行文本,不過這行文本沒有被回射。

圖8-10給出了tcpdump的輸出。

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——8.9 伺服器程式未運作

首先我們注意到,在客戶主機能夠往伺服器主機發送那個udp資料報之前,需要一次arp請求和應答的交換。(我們把這個交換保留在tcpdump的輸出中,是為了強調在ip資料報可發往本地網絡上另一個主機或路由器之前,還是有可能出現arp請求-應答的。)

我們從第3行看到客戶資料報發出,然而從第4行看到,伺服器主機響應的是一個"port unreach-able"(端口不可達)icmp消息。(長度13是12個字元加換行符。)不過這個icmp錯誤不傳回給客戶程序,其原因我們稍後講述。客戶永遠阻塞于圖8-8中的recvfrom調用。我們還指出icmpv6也有端口不可達錯誤類型,類似于icmpv4(見圖a-15和圖a-16),是以這裡讨論的結果對于ipv6也類似。

我們稱這個icmp錯誤為異步錯誤(asynchronous error)。該錯誤由sendto引起,但是sendto本身卻成功傳回。回顧2.11節,我們知道從udp輸出操作成功傳回僅僅表示在接口輸出隊列中具有存放所形成ip資料報的空間。該icmp錯誤直到後來才傳回(圖8-10所示為4ms之後),這就是稱其為異步的原因。

一個基本規則是:對于一個udp套接字,由它引發的異步錯誤卻并不傳回給它,除非它已連接配接。我們将在8.11節讨論如何給udp套接字調用connect。很少有人明白套接字最初實作時為什麼做此設計決策。(實作内涵在tcpv2第748~749頁讨論。)

考慮在單個udp套接字上接連發送3個資料報給3個不同的伺服器(即3個不同的ip位址)的一個udp客戶。該客戶随後進入一個調用recvfrom讀取應答的循環。其中有2個資料報被正确遞送(也就是說,3個主機中有2個在運作伺服器),但是第三個主機沒有運作伺服器。第三個主機于是以一個icmp端口不可達錯誤響應。這個icmp出錯消息包含引起錯誤的資料報的ip首部和udp首部。(icmpv4和icmpv6出錯消息總是包含ip首部和所有的udp首部或部分tcp首部,以便其接收者确定由哪個套接字引發該錯誤,如圖28-21和圖28-22所示。)發送這3個資料報的客戶需要知道引發該錯誤的資料報的目的位址以區分究竟是哪一個資料報引發了錯誤。但是核心如何把該資訊傳回給客戶程序呢?recvfrom可以傳回的資訊僅有errno值,它沒有辦法傳回出錯資料報的目的ip位址和目的udp端口号。是以做出決定:僅在程序已将其udp套接字連接配接到恰恰一個對端後,這些異步錯誤才傳回給程序。

隻要so_bsdcompat套接字選項沒有開啟,linux甚至對未連接配接的套接字也傳回大多數icmp“destination unreachable”(目的地不可達)錯誤。圖a-15中除代碼為0、1、4、5、11和12之外的所有icmp目的地不可達錯誤均被傳回。

我們将在28.7節再次讨論udp套接字上異步錯誤的這個問題,并給出一個使用我們自己的守護程序擷取未連接配接套接字上這些錯誤的簡便方法。

繼續閱讀