【TCP/IP详解 卷一:协议】第十八章 TCP连接 的建立与终止 (2)其余内容
18.5 TCP的半關閉
牢記 TCP 是 全雙工 的。
半關閉:TCP提供了連接的一端 在結束了它的發送后 還能接收來自另外一端數據的能力。但是只有很少的應用程序利用它。
為了實現這個特性,編程接口必須提供一種方法來說明“我已經完成了數據的傳送,并且發了FIN給另外一端,但是我還是想接收另外一端發送來的數據,直到結束(向我發送FIN)”。
在 執行半關閉 的一端 收到來自另外一端的 FIN 之后,傳送一個EOF給應用程序,并對這個 FIN 進行確認并發送ACK,結束了這個連接。
18.6 重點:TCP的狀態變遷圖
參考:TCP的狀態變遷圖
狀態:描述
CLOSED:無連接是活動的或正在進行
LISTEN:服務器在等待進入呼叫
SYN_RECV:一個連接請求已經到達,等待確認 被動打開
SYN_SENT:應用已經開始,打開一個連接 主動打開
ESTABLISHED:正常數據傳輸狀態
FIN_WAIT1:應用說它已經完成
FIN_WAIT2:另一邊已同意釋放
ITMED_WAIT:等待所有分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另一邊已初始化一個釋放
LAST_ACK:等待所有分組死掉
注意:第一點,我們使用粗的實線箭頭表示正常的客戶端狀態變遷,使用虛線箭頭表示正常的服務器狀態變遷。
第二點是兩個導致進入 ESTABLISHED 狀態的變遷 對應打開一個連接(SYN_SENT & SYN_RCVD/SYN_RECV),和兩個導致ESTABLISHED狀態離開的變遷 對應關閉一個連接(FIN_WAIT1 & FIN_WAIT2)。
主動打開:SYN_SENT 被動打開:SYN_RECV
主動關閉:FIN_WAIT1 FIN_WAIT2 TIME_WAIT CLOSING
被動關閉:CLOSE_WAIT LAST_ACK
課程通過 TCP時序圖來說明
我們執行被動關閉(進入LISTEN),收到一個SYN,發送一個帶ACK的SYN(進入SYN_RCVD),然后收到一個RST,而不是一個ACK,便又回到LISTEN狀態并等待另外一個連接請求的到來。
左邊的客戶執行主動打開,而右邊的服務器執行被動打開。圖中顯示客戶端執行主動關閉,但是正如我們之前提到的,另外一端也可以執行主動關閉。
18.6.1 2MSL等待時間
TIME_WAIT狀態也稱為2MSL等待狀態。
MSL:TCP實現選擇的 一個報文段最大生存時間MSL。與IP的TTL類似。
對IP數據報TTL的限制是 基于跳數,而不是 定時器。
當TCP執行一個主動關閉 并且 發送最后一個ACK(由主動關閉端發送),該連接在 TIME_WAIT狀態 停留的時間為2倍的MSL。這樣如果最后的ACK丟失 -> 另外一端(被動關閉)超時 重新發送FIN -> 可以讓TCP重發最后一個ACK。
在連接處于2MSL狀態的時候,任何遲到的報文段都將被丟棄。因為處在2MSL狀態等待的,由socket pair(目的IP地址,源IP地址,目的端口號,源端口號)定義的連接在這段時間內不能再次使用。
一個連接由socket pair定義,一個連接的新的實例(instance)稱為該連接的替身。
前文有提到,當要建立一個有效的連接的時候,來自該連接的 一個較早替身 的遲到報文段(SYN/ACK...) 為了防止它作為 使用相同socket pair的新連接 的一部分 將會被曲解。
大概意思是:來自舊連接的 遲到的報文段,為了防止被當成 新連接(采用相同socket pair)的一部分,會被銷毀。舊的東西不能被當成新的東西,應該銷毀。
如果我們一直重啟服務器程序,并測量它到成功的時間,我們就可以確定2MSL值。
18.6.2 平靜時間
對于來自 某個連接的較早替身 的遲到報文段,2MSL等待可以 防止將它解釋成 使用相同接口對(socket pair)的新連接 的一部分(就是防止解釋成另外一個連接的部分)。
平靜時間:TCP在重新啟動的MSL秒內不能建立任何連接。
防止的是:處于2MSL等待狀態的主機重新啟動,如果使用 處于2MSL的socket pair(確定一個連接) 來建立一個新的連接,那么任何 之前遲到的(舊連接的)報文段 都會被錯誤的當成新連接的一部分。
18.6.3 FIN_WAIT_2狀態
FIN_WAIT_2: 在主動關閉端 發送了第一個FIN 并且 接收到了ACK 之后,進入FIN_WAIT_2狀態。
只有當:另外一端的應用層 意識到它已經接收了一個文件結束符(EOF),并且向我們發送 第二個FIN 來關閉另外一個方向的連接時,在主動關閉端接收到第二個FIN之后,我們才會從FIN_WAIT_2狀態 轉換到 TIME_WAIT狀態。
18.7 復位報文段
TCP首部的 RST比特 是用于復位的。無論什么時候,發送往 基準的連接(即由socket pair確定的連接) 的報文段 出現錯誤,TCP都會發出一個復位報文段。
有以下三種情況:
- 對方端口不存在
- 異常關閉
- 檢查半打開連接
18.7.1 對方端口不存在
當連接請求到達時,目的端口沒有進程正在LISTEN。這個時候,對于UDP:產生一個ICMP端口不可達報文。對于TCP:使用復位。
18.7.2 異常關閉
終止一個連接的正常方法是:發送FIN,進行四次揮手。稱為 有序釋放。
但是也有可能發送一個復位數據報而不是FIN來 中途釋放一個連接。稱為 異常釋放。
異常終止一個連接 對應用程序的兩個優點:
- 丟棄任何待發數據報并立刻發送復位報文段。
- RST的接收方會 區別 另外一端執行的是 異常關閉 還是 正常關閉,應用程序使用的API 必須 提供 產生異常關閉 而不是正常關閉 的手段。
需要注意的是:RST報文段 不會導致另外一端產生任何響應,另外一端根本不進行確認,收到RST的一端將終止該連接,并通知應用層連接進行復位。
19.7.3 檢查半打開連接
半打開:一方已經關閉或者異常終止連接,而另外一方仍然不知道,這樣的TCP連接稱作 半打開。
常見原因:客戶主機突然掉電。
我們異常關閉服務器,然后重新啟動,讓客戶端向服務器發送數據(斷電之前有連接),由于服務器的TCP已經重新啟動,它將丟失 復位 之前連接的所有信息,因此它不知道客戶端新發送的數據報中 提到的連接。
這個時候,TCP的處理是:接收方以復位作為應答。
18.8 同時打開
每一方必須發送一個SYN,并且這些SYN必須傳遞給對方。這需要每一方使用一個對方熟知的端口 作為本地端口。稱為同時打開。
比如 主機A中 一個應用程序使用 7777本地端口,與主機B的端口8888 執行主動打開。B中的應用程序使用 8888本地端口,并與A中的端口7777 執行主動打開。
每一端 既是服務器 又是客戶端。
tcp協議在遇到這種情況時,只會打開一條連接。(其他,比如OSI七層模型中的傳輸層 使用的是兩條連接)
這個連接的建立過程需要4次數據交換,而一個典型的連接建立只需要3次交換(即3次握手)。
18.9 同時關閉
雙方都執行主動關閉,同時關閉和正常關閉使用的 段交換數目 相同。
18.11 TCP服務器的設計
回顧 UDP服務器的設計,大多數UDP服務器是 重復型 的,這意味著 單個服務器的進程 對 單個UDP端口上 的所有客戶請求進行處理。每一個UDP端口都與一個有限大小的輸入隊列相聯系。
大多數TCP服務器是 并發的。當一個新的連接請求到達服務器的時候,服務器接受這個請求,并調用一個新進程 來處理這個新的客戶請求,不同的操作系統使用不同的技術來調用新的服務器進程。也可以使用輕型進程 -線程(thread)。
18.11.1 TCP服務器端口
解決問題:TCP如何處理端口號?
不同的連接請求,有可能使用相同的服務器端口號,但是需要我們注意的一點是(或者說 應該牢記的是):TCP通過socket pair來確定一條連接。也就是說,TCP通過本地IP地址,源IP地址,本地端口號,源端口號 “四人幫”來處理多個連接請求。
18.11.2 & 18.11.3 限定的本地IP地址 & 限定的源端IP地址
介紹了 服務器必須使用 特定的本地IP地址 和 服務器指定遠端IP地址和遠端端口號 的情況。
18.11.4 呼入連接請求隊列
一個并發型服務器 調用一個新的進程 來處理客戶的連接請求,因此 處于被動連接請求 的服務器應該始終準備處理下一個 呼入的連接請求。
解決問題:服務器正忙的時候,如何處理新的請求?
(1)在等待連接的一端(服務器端)有一個固定長度的連接隊列,該隊列中的連接已經被TCP接受(完成三次握手),但是還沒有被應用程序接受。
(2)積壓值:應用層指明的 隊列最長長度。取值范圍:0-5
(3)我們期望:積壓值 = 這一端點所能接受的最多連接。但是事實是 并不相等。
(4)隊列還有空間的時候,TCP確認完之后,把新的請求放入隊列中。
(5)隊列沒有空間了,TCP不理會新的連接請求,也不發會 RST。最終 客戶端的主動打開會超時。
注意點:
- 什么時候進隊列?-答:TCP確認完畢之后,即三次握手完成。
- 什么時候出隊列?-答:應用層知道了這個連接,并接收了它。
- 什么是積壓值?-答:隊列最長長度,所能容納的最多連接請求數。
2016/8/13
總結
以上是生活随笔為你收集整理的【TCP/IP详解 卷一:协议】第十八章 TCP连接 的建立与终止 (2)其余内容的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux图机界面机制
- 下一篇: 写一个脚本,判断下如果是阴历7月7日,在