1. 程式人生 > >Linux TCP 伺服器程式設計(五):埠重用及TIME_WAIT

Linux TCP 伺服器程式設計(五):埠重用及TIME_WAIT

上一節說到設定套接字選項的問題,我們這一節主要處理上一節說的“Address already in use”問題,這個問題通過設定設定一個套接字選項來解決。

這個錯誤提示是在bind函式呼叫時發生的,bind函式經常遇到這個問題:試圖繫結一個已經在使用的埠。但是我們已經明確關閉程序了,這是有套接字狀態TIME_WAIT引起的,該狀態在套接字關閉後幾分鐘仍保留,在TIME_WAIT狀態退出之後,套接字被刪除,該地址才能重新繫結而不出問題。

首先介紹一下設定套接字選項函式setsockopt():

函式原型:

#include <sys/socket.h>
int setsockopt(int sockfd,int level, int optname, const void optval, socklen_t optlen);

sockfd - 開啟的描述符。

level - 解釋選項程式碼或特定協議的程式碼,如SLO_SOCKET。

optname - 套接字選項,如SO_REUSEADDR。

optval - 某變數指標。

optlen - 某變數長度。 我們只需在bind函式之前呼叫該函式即可.

int	on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSERADDR, &on, sizeof(on);

重新編譯,執行,發現可以解決該問題。

 接下來說說TIME_WAIT:

主動關閉的socket端會進入TIME_WAIT狀態,並且持續2MSL時間長度,MSL就是maximum segment lifetime(最大分節生命期),這是一個IP資料包能在網際網路上生存的最長時間,超過這個時間將在網路中消失。MSL在RFC 1122上建議是2分鐘,而源自berkeley的TCP實現傳統上使用30秒,因而,TIME_WAIT狀態一般維持在1-4分鐘。

  IP頭部有一個TTL,最大值255。儘管TTL的單位不是秒(根本和時間無關),我們仍需 假設,TTL為255的TCP報文在Internet上生存時間不能超過MSL。  TCP報文在傳送過程中可能因為路由故障被迫緩衝延遲、選擇非最優路徑等等,結果 傳送方TCP機制開始超時重傳。前一個TCP報文可以稱為"漫遊TCP重複報文",後一個 TCP報文可以稱為"超時重傳TCP重複報文",作為面向連線的可靠協議,TCP實現必須 正確處理這種重複報文,因為二者可能最終都到達。 

一個通常的TCP連線終止可以用圖描述如下:
client                               server

FIN M

close  -----------------> (被動關閉) 

                                      ACK M+1
            <----------------- 

                                       FIN N

            <-----------------  close

ACK N+1
            -----------------> 

為什麼需要 TIME_WAIT 狀態? 假設最終的ACK丟失,server將重發FIN,client必須維護TCP狀態資訊以便可以重發
最終的ACK,否則會發送RST,結果server認為發生錯誤。TCP實現必須可靠地終止連 接的兩個方向(全雙工關閉),client必須進入 TIME_WAIT 狀態,因為client可能面 臨重發最終ACK的情形。

TIME_WAIT狀態存在的理由:

1)可靠地實現TCP全雙工連線的終止

     在進行關閉連線四路握手協議時,最後的ACK是由主動關閉端發出的,如果這個最終的ACK丟失,伺服器將重發最終的FIN,因此客戶端必須維護狀態資訊允許它重發最終的ACK。如果不維持這個狀態資訊,那麼客戶端將響應RST分節,伺服器將此分節解釋成一個錯誤。因而,要實現TCP全雙工連線的正常終止,必須處理終止序列四個分節中任何一個分節的丟失情況,主動關閉的客戶端必須維持狀態資訊進入TIME_WAIT狀態。
2)允許老的重複分節在網路中消逝

      TCP分節可能由於路由器異常而“迷途”,在迷途期間,TCP傳送端可能因確認超時而重發這個分節,迷途的分節在路由器修復後也會被送到最終目的地,這個原來的迷途分節就稱為lost duplicate。在關閉一個TCP連線後,馬上又重新建立起一個相同的IP地址和埠之間的TCP連線,後一個連線被稱為前一個連線的化身(incarnation),那麼有可能出現這種情況,前一個連線的迷途重複分組在前一個連線終止後出現,從而被誤解成從屬於新的化身。為了避免這個情況,TCP不允許處於TIME_WAIT狀態的連線啟動一個新的化身,因為TIME_WAIT狀態持續2MSL,就可以保證當成功建立一個TCP連線的時候,來自連線先前化身的重複分組已經在網路中消逝。