天天看點

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——1.5 一個簡單的時間擷取伺服器程式

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

我們可以編寫一個簡單的tcp時間擷取伺服器程式,它和1.2節中的客戶程式一道工作。圖1-9給出了這個伺服器程式,它使用了上一節中講過的包裹函數。

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——1.5 一個簡單的時間擷取伺服器程式

建立tcp套接字

10 tcp套接字的建立與客戶程式相同。

把伺服器的衆所周知端口捆綁到套接字

11~15 通過填寫一個網際套接字位址結構并調用bind函數,伺服器的衆所周知端口(對于時間擷取服務是13)被捆綁到所建立的套接字。我們指定ip位址為inaddr_any,這樣要是伺服器主機有多個網絡接口,伺服器程序就可以在任意網絡接口上接受客戶連接配接。以後我們将了解怎樣限定伺服器程序隻在單個網絡接口上接受客戶連接配接。

把套接字轉換成監聽套接字

16 調用listen函數把該套接字轉換成一個監聽套接字,這樣來自客戶的外來連接配接就可在該套接字上由核心接受。socket、bind和listen這3個調用步驟是任何tcp伺服器準備所謂的監聽描述符(listening descriptor,本例中為listenfd)的正常步驟。

常值listenq在我們的unp.h頭檔案中定義。它指定系統核心允許在這個監聽描述符上排隊的最大客戶連接配接數。我們将在4.5節詳細說明客戶連接配接的排隊。

接受客戶連接配接,發送應答

17~21 通常情況下,伺服器程序在accept調用中被投入睡眠,等待某個客戶連接配接的到達并被核心接受。tcp連接配接使用所謂的三路握手(three-way handshake)來建立連接配接。握手完畢時accept傳回,其傳回值是一個稱為已連接配接描述符(connected descriptor)的新描述符(本例中為connfd)。該描述符用于與新近連接配接的那個客戶通信。accept為每個連接配接到本伺服器的客戶傳回一個新描述符。

本書全文采用的無限循環采用以下風格:

目前時間和日期是由庫函數time傳回的,它實際上傳回的是自unix紀元即0點0分0秒(國際标準時間)以來的秒數。下一個庫函數ctime把該整數值轉換成直覺可讀的時間格式,例如:

<code>mon may 26 20:58:40 2003</code>

snprintf函數在這個字元串末尾添加一個回車符和一個回行符,随後write函數把結果字元串寫給客戶。

如果你尚不習慣改用snprintf代替較早的sprintf函數,那麼現在是學習的時候了。調用sprintf無法檢查目的緩沖區是否溢出。相反,snprintf要求其第二個參數指定目的緩沖區的大小,是以可確定該緩沖區不溢出。

snprintf相對較晚才加到ansi c标準中,在稱為iso c99的版本中引入。不過幾乎所有廠商都把它作為标準c函數庫的一部分提供,而且另有許多免費可得的版本可用。我們貫穿全書使用snprintf,也推薦你出于可靠性考慮在自己的程式中改用它來代替sprintf。

值得注意的是,許多網絡入侵是由黑客通過發送資料,導緻伺服器對sprintf的調用使其緩沖區溢出而發生的。必須小心使用的函數還有gets、strcat和strcpy,通常應分别改為調用fgets、strncat和strncpy。更好的替代函數是後來才引入的strlcat和strlcpy,它們確定結果是正确終止的字元串。編寫安全的網絡程式的更多技巧參見[garfinkel, schwartz, and spafford 2003]的第23章。

終止連接配接

22 伺服器通過調用close關閉與客戶的連接配接。該調用引發正常的tcp連接配接終止序列:每個方向上發送一個fin,每個fin又由各自的對端确認。2.6節将詳細講述tcp的三路握手和用于終止一個tcp連接配接的4個tcp分組。

與上節檢視客戶程式一樣,本節檢視伺服器程式也非常簡略,具體細節留待本書以後論述。有以下幾點需要注意。

與其客戶程式一樣,這一伺服器程式也與ipv4協定相關。我們将在圖11-13中給出使用getaddrinfo函數實作的一個協定無關的版本。

本伺服器一次隻能處理一個客戶。如果多個客戶連接配接差不多同時到達,系統核心在某個最大數目的限制下把它們排入隊列,然後每次傳回一個給accept函數。本伺服器隻需調用time和ctime這兩個庫函數,運作速度很快。然而如果伺服器需用較多時間(譬如說幾秒鐘或一分鐘)服務每個客戶,那麼我們必須以某種方式重疊對各個客戶的服務。

圖1-9中所示的伺服器稱為疊代伺服器(iterative server),因為對于每個客戶它都疊代執行一次。同時能處理多個客戶的并發伺服器(concurrent server)有多種編寫技術。最簡單的技術是調用unix的fork函數(4.7節),為每個客戶建立一個子程序。其他技術包括使用線程代替fork(26.4節),或在伺服器啟動時預先fork一定數量的子程序(30.6節)。

如果從shell指令行啟動本例這樣的一個伺服器,我們也許想要它運作很長時間,因為伺服器往往在系統工作期間一直運作。這要求我們往伺服器程式中添加代碼,以便它能夠作為一個unix守護程序(daemon)——能在背景運作且不跟任何終端關聯的程序——運作。我們将在13.4節讨論守護程序。

繼續閱讀