閱讀877 返回首頁    go 技術社區[雲棲]


TCP協議詳解(理論篇)

本文章摘自紅黑聯盟 https://www.2cto.com/net/201208/149347.html 供大家學習交流

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)滑動窗口——發送端(擁塞窗口)
     網絡擁塞出現的原因是多方麵的,除了收發兩段的硬件差異之外,還與網絡通信鏈路相關,如通信鏈路中轉發路由器的緩存,試想這樣一種情況,若發送端與接收端的處理能力以及吞吐能力很強,若它僅僅通過接收端所返回的滑動窗口大小,難以阻止數據報在被路由器轉發過程中因為阻塞而被丟棄的情形,因為與發送端相連的路由器因為自身的緩衝空間的限製,難以存儲並轉發這麼多數據而出現丟包現象。那麼這種情況如何避免呢?最好的機製是中間的路由器也要參與給發送端反饋窗口大小的事情,也正是本節所說的擁塞窗口。
      綜合上麵的闡述,發送端會收到兩個窗口大小,分別是來自於接收端和中間路由器,注意前者在每次的數據報中都會出現,而後者隻是在網絡中出現擁塞時由中間路由器發送。那麼,發送方此時取接收端的窗口大小與擁塞窗口中的最小值作為發送上限值。

最後更新:2017-04-03 12:55:01

  上一篇:go Visual Studio 程序集 詳解
  下一篇:go Gartner預測大數據分析人才將供不應求