1. 程式人生 > >TCP協議詳解(理論篇)

TCP協議詳解(理論篇)


1.    與UDP不同的是,TCP提供了一種面向連線的、可靠的位元組流服務。面向連線比較好理解,就是連線雙方在通訊前需要預先建立一條連線,這猶如實際生活中的打電話。助於可靠性,TCP協議中涉及了諸多規則來保障通訊鏈路的可靠性,總結起來,主要有以下幾點:       (1)應用資料分割成TCP認為最適合傳送的資料塊。這部分是通過“MSS”(最大資料包長度)選項來控制的,通常這種機制也被稱為一種協商機制,MSS規定了TCP傳往另一端的最大資料塊的長度。值得注意的是,MSS只能出現在SYN報文段中,若一方不接收來自另一方的MSS值,則MSS就定為536位元組。一般來講,在不出現分段的情況下,MSS值還是越大越好,這樣可以提高網路的利用率。       (2)重傳機制。設定定時器,等待確認包。       (3)對首部和資料進行校驗。       (4)TCP對收到的資料進行排序,然後交給應用層。       (5)TCP的接收端丟棄重複的資料。       (6)TCP還提供流量控制。(通過每一端宣告的視窗大小來提供的) 2. TCP包的首部:  www.2cto.com  

  (1)      若不計選項欄位,TCP的首部佔20個位元組。 (2)      源埠號以及目的埠號用於尋找發端和接收端的程序,一般來講,通過埠號和IP地址,可以唯一確定一個TCP連線,在網路程式設計中,通常被稱為一個socket介面。 (3)      序號是用來標識從TCP發端向TCP接收端傳送的資料位元組流。 (4)      確認序號包含傳送確認的一端所期望收到的下一個序號,因此,確認序號應該是上次已經成功收到資料位元組序號加1. (5)      首部長度指出了TCP首部的長度值,若不存在選項,則這個值為20位元組。 (6)      標誌位(flag):                 URG: 緊急指標有效                 ACK:確認序號有效                 PSH:接收方應儘快將這個報文段交給應用層                 RST:重建連線                 SYN:同步序號用來發起一個連線                 FIN: 發端完成傳送任務(主動關閉)    
                     【解釋】 ◆     TCP提供解決方式是為了使一端告訴另外一端某些“緊急資料”已經放置在普通的資料流中,讓接收端對緊急資料做特別處理。此時,URG位被置為1,並且16位的緊急資料被置為一個正的偏移量,通過此偏移量與TCP首部中的序號欄位相加,可以得出緊急資料的最後一個位元組的序號,常見的應用有傳輸中斷鍵(在通過telnet連線過程中)。 ◆     RST: 復位欄位被用於當一個報文傳送到某個socket介面而出現錯誤時,TCP則會發出復位報文段。常見出現的情況有以下幾種:  www.2cto.com   傳送到不存在的埠的連線請求:此時對方的目的埠並沒有偵聽,對於UDP,將會發出ICMP不可達的   錯誤資訊,而對於TCP,將會發出設定RST復位標誌位的資料報。異常終止一個連線:正常情況下,通過傳送FIN去正常關閉一個TCP連線,但也有可能通過傳送一個復位   報文段去中途釋放掉一個連線。在socketAPI中通過設定socket選 項SO_LINGER去關閉這種異常關閉的情況。 3. TCP的連線與終止過程:

  (1)  三次握手:          建立一個TCP連線,必須經歷三次握手過程,其中傳送第一個SYN的一端將執行主動開啟,接收這個SYN併發回下一個SYN的另一端執行被動開啟。 (2)  四次釋放:          要釋放一個TCP連線,需要通過四次握手過程,這是由TCP的半關閉特性造成的,因為TCP連線時全雙工的,因此,需要TCP兩端要單獨執行關閉。值得注意的是,主動關閉的一端在傳送FIN之後,依然還能正常接收對方的資料,只是通知對方它已經沒有資料需要傳送了,同理,被動關閉的一端在收到FIN之後,仍然可以傳送資料,直到它自身同樣發出FIN之後,才停止傳送資料。
  (3)  TCP連線的超時問題:          完成一個TCP連線,中間涉及到一個超時的問題,大多數伯克利系統的超時時限為75s,Solaris9的超時時限為240s,因此,一般認為是在75-240之間。 【引申】在具體的實現中,如何由使用者自己去完成設定socket連線超時時間? 【解決方法】目前實現socket超時連線主要是通過select來完成的。具體步驟如下:     ◆     建立socket     ◆     將socket設定為非阻塞模式(若是阻塞模式,那麼時間設定就毫無意義)     ◆     呼叫connect去進行連線     ◆     使用select檢查socket是否可寫,並同時判斷其結果(為什麼是可寫?因為需要檢測socket是否收到ACK。)     ◆     將socket轉化為阻塞模式
  (4)TCP的半關閉  www.2cto.com            所謂“半關閉”,是指連線的一端在結束它的傳送之後還能接收到對方發過來的資料的能力。具體表現在,當完成三次握手的雙方,其中有一端發出FIN,此時它將進入半關 閉狀態,此時它關閉了自身的傳送功能,但是它依然可以接收到對方的資料,如對方發過來的ACK訊息。那麼在實際開發中,是怎麼實現的呢? 這牽涉到系統中shutdown和close函式的區別問題。         int shutdown(int s, int how)       <sys/socket.h>          shutdown是用來終止引數s所指定的socket介面,引數how主要有以下幾種情況:          how = 0    終止讀取操作          how = 1    終止寫入操作          how = 2    終止讀取和寫入操作          返回的errorcode可能有:          EBADF  /* Bad file descriptor */          ENOTSOCK  /* Socket operation on non-socket */          ENOTCONN  /* Socket is not connected */
  【引用】     Big difference between shutdown and close on a socket is thebehavior when the socket is shared by other processes.A shutdown() affects all copies of the socket whileclose() affects only the file descriptor in one process.     Even if you close() a TCP socket, it won't necessarily beimmediately reusable anyway, since it will be in a TIME_WAITstate while the OS makes sure there's no outstandingpackets that might get confused as new information if you were to immediatelyreuse that socket for something else. 【注意】 當shutdown關閉讀取部分時,則會丟掉接收緩衝區中的任何資料,並關閉該端的連線,若是關閉寫入部分,TCP則會發送剩餘的資料,然後終止連線的寫入端。 4. TCP的狀態變遷圖:

  幾個狀態解析: (1)     TIME_WAIT狀態     這種狀態也稱為2MSL等待狀態,MSL即一個報文段的最長生存時間,也就是報文在網路中被丟棄前的最長時間。那麼為什麼需要等待2倍的MSL呢?這是因為在TIME_WAIT狀態之後,需要執行主動關閉,傳送ACK,同時還需要加上一倍的MSL,為了等待對方的反饋結果(是否收到重發的FIN),這是因為再發送ACK之後,可能因為諸多原因而導致ACK傳送失敗,此時Server端會在此傳送FIN。      正常情況下,client在2MSL期間,對應的socket是不能再被使用的,但是在具體的實現中(如伯克利),則可以通過SO_REUSEADDR選項重用此介面。 (2)     FIN_WAIT_2狀態      當對方對自己傳送的FIN進行了確認,此時將進入FIN_WAIT_2狀態。 (3) CLOSE_WAIT狀態與FIN_WAIT_1狀態      當連線中的一方收到對方發過來的FIN時,它將進入CLOSE_WAIT狀態,而另一端則進入FIN_WAIT_1狀態。  www.2cto.com   5. TCP中流量控制機制——滑動視窗       受到諸多因素的影響,如硬體(雙方網絡卡吞吐量差異)、網路環境,網路極易出現各種各樣的擁塞,目前所採取的措施主要有以下兩種:改進擁塞演算法以及控制傳送端和接收端的流量。本節主要講述如何在接收端和傳送端進行流量控制。 (1)滑動視窗——接收端       在講解滑動視窗協議之前,可以回顧下最初人們為了在接收端實施流量控制所提出了的經典演算法——停止等待演算法,其核心思想是:接收端在收到一個數據報之後,停止接收新的資料報,直到發出ACK(對收到的資料報的確認)之後進行恢復。演算法思想以及實現非常簡單,但是卻遇到一個效率的問題,特別是隨著網路裝置的資料處理能力得到了很大的提高,效率低下顯得尤其明顯,後來人們也嘗試了多種改進措施,如本節的滑動視窗就是其中之一。       其基本原理是:在接收端存在一個接收快取區,用來接收來自於傳送方的資料,只有當應用程序從接收快取區中取出資料(可能只是部分)併發出其ACK後,才算作這部分資料已經接收,然後調節此時的滑動視窗大小。傳送方根據返回的視窗大小,計算出所能傳送的資料大小。因此,可以這麼理解,滑動視窗演算法是接收端作為主動方根據自身的快取以及處理能力主動去調節對方的傳送流量的一種調節演算法。       下面通過一個滑動視窗模型圖瞭解下發送方是如何處理所收到的滑動視窗的?

  在傳送方依然存在一個快取區(傳送快取區),其傳送的資料可以是下面幾種狀態: ◆ 傳送並確認 (1-3) ◆ 傳送但未確認 (4-6)  www.2cto.com   ◆ 可以傳送 (7-9) ◆ 不能夠傳送 (10以後)       值得注意的是,滑動視窗是基於所收到的確認序列號的。當傳送方根據所收到的確認序列號以及視窗大小,不斷向後移動,並相應更新資料狀態。 (2)滑動視窗——傳送端(擁塞視窗)      網路擁塞出現的原因是多方面的,除了收發兩段的硬體差異之外,還與網路通訊鏈路相關,如通訊鏈路中轉發路由器的快取,試想這樣一種情況,若傳送端與接收端的處理能力以及吞吐能力很強,若它僅僅通過接收端所返回的滑動視窗大小,難以阻止資料報在被路由器轉發過程中因為阻塞而被丟棄的情形,因為與傳送端相連的路由器因為自身的緩衝空間的限制,難以儲存並轉發這麼多資料而出現丟包現象。那麼這種情況如何避免呢?最好的機制是中間的路由器也要參與給傳送端反饋視窗大小的事情,也正是本節所說的擁塞視窗。       綜合上面的闡述,傳送端會收到兩個視窗大小,分別是來自於接收端和中間路由器,注意前者在每次的資料報中都會出現,而後者只是在網路中出現擁塞時由中間路由器傳送。那麼,傳送方此時取接收端的視窗大小與擁塞視窗中的最小值作為傳送上限值。