#
#服務器
TCP協議提供可靠的面向鏈接服務,採用三次握手創建鏈接。
第一次握手:創建鏈接時,客戶端發送SYN包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到SYN包,向客戶端返回ACK(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RCVD狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據,也就是ESTABLISHED狀態。 網絡
#併發
採用四次揮手斷開雙向鏈接。
(1) TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送。
(2) 服務器收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1。和SYN同樣,一個FIN將佔用一個序號。
(3) 服務器關閉客戶端的鏈接,發送一個FIN給客戶端。
(4) 客戶端發回ACK報文確認,並將確認序號設置爲收到序號加1。socket
**三次握手**是`TCP/IP`創建鏈接的確認機制,是客戶端和服務器雙方要確認對方能收到本身的信息,而**三次握手**是最短路徑。 1. 第一次握手:創建鏈接時,`Client`發送請求報文段(`syn seq=x`),進入`SYN_SENT`狀態,等待`Server`確認。 2. 第二次握手:服務器收到,`Server`回覆信息給`Client`,包含給客戶端的確認信息,和服務器自身的報文信息(`ack=x+1 syn seq=y`),此時服務器進入`SYN_RECV`狀態。 3. 第三次握手:客戶端收到`SYN+ACK`包,`Client`回覆服務器(`ack=y+1`),客戶端和服務器進入`ESTABLISHED`(TCP鏈接成功)狀態,完成三次握手。 三次握手完成後,客戶端與服務器開始傳送數據。
形象比喻ide
- A 向 B 發起創建鏈接請求:A——>B;
- B 收到 A 的發送信號,而且向A發送確認信息:B——>A;
- A 收到 B 的確認信號,並向B發送確認信號:A——>B。
經過第一次握手,B 知道 A 可以發送數據。經過第二次握手,A 知道 B 能發送數據。
結合第一次握手和第二次握手,A 知道 B 能接收數據。結合第三次握手,B知道A可以接收數據。測試至此,完成了握手過程,A 知道 B 能收能發,B 知道 A 能收能發,通訊鏈接至此創建。
三次鏈接是保證可靠的最小握手次數,再屢次握手也不能提升通訊成功的機率,反而浪費資源。ui
**四次揮手**是`TCP/IP`關閉鏈接的確認機制。 對於一個已經創建的鏈接,TCP使用改進的三次握手來釋放鏈接(使用一個帶有FIN附加標記的報文段)。TCP關閉鏈接的步驟以下: 1. 第一步:當主機 **A** 的應用程序通知TCP數據已經發送完畢時,TCP向主機B發送一個帶有`FIN`附加標記的報文段(FIN表示英文finish)。 2. 第二步:主機 **B** 收到這個`FIN`報文段以後,並不當即用**FIN**報文段回覆主機A,而是先向主機A發送一個確認序號**ACK**,同時通知本身相應的應用程序:對方要求關閉鏈接(先發送ACK的目的是爲了防止在這段時間內,對方重傳FIN報文段)。 3. 第三步:主機 **B** 的應用程序告訴**TCP**:我要完全的關閉鏈接,**TCP**向主機 **A**送一個`FIN`報文段。 4. 第四步:主機 **A** 收到這個 `FIN` 報文段後,向主機 **B** 發送一個`ACK`表示鏈接完全釋放。
形象比喻操作系統
- A 向 B 發起請求(fin),表示A沒有數據要發送了:A——>B;
- B 向 A 發送信號(ack),知道A要請求斷開;
- B 向 A 發送信號(fin),數據接收完成,告知B要關閉鏈接:B——>A;
- A 向 B 收到信號(fin)向B發送(ack),確認斷開:A——>B。
- B 收到確認信號,斷開鏈接,而A在一段時間內沒收到B的信號,代表B已經斷開了,因而A也斷開了鏈接。至此,完成揮手過程。
2,3次揮手不能合爲一次,由於此時 A 雖然再也不發送數據了,可是還能夠接收數據,B可能還有數據要發送給A,因此兩次揮手不能合併爲一次。
揮手次數比握手多一次,是由於握手過程,通訊只須要處理鏈接。而揮手過程,通訊須要處理數據+鏈接。code
(1)客戶端發送一個帶SYN標誌的TCP報文到服務器。這是三次握手過程當中的報文1。
(2) 服務器端迴應客戶端的,這是三次握手中的第2個報文,這個報文同時帶ACK標誌和SYN標誌。所以它表示對剛纔客戶端SYN報文的迴應;同時又標誌SYN給客戶端,詢問客戶端是否準備好進行數據通信。
(3) 客戶必須再次迴應服務段一個ACK報文,這是報文段3。blog
因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的鏈接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP鏈接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。
(1) TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送(報文段4)
(2) 服務器收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN同樣,一個FIN將佔用一個序號
(3) 服務器關閉客戶端的鏈接,發送一個FIN給客戶端(報文段6)
(4) 客戶段發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)
`CLOSED`:這個沒什麼好說的了,表示初始狀態。 `LISTEN`:這個也是很是容易理解的一個狀態,表示服務器端的某個SOCKET處於監聽狀態,能夠接受鏈接了。 `SYN_RCVD`:這個狀態表示接受到了SYN報文,在正常狀況下,這個狀態是服務器端的SOCKET在創建TCP鏈接時的三次握手會話過程當中的一箇中間狀態,很短暫,基本上用netstat你是很難看到這種狀態的,除非你特地寫了一個客戶端測試程序,故意將三次TCP握手過程當中最後一個ACK報文不予發送。所以這種狀態時,當收到客戶端的ACK報文後,它會進入到ESTABLISHED狀態。 `SYN_SENT`:這個狀態與SYN_RCVD遙想呼應,當客戶端SOCKET執行CONNECT鏈接時,它首先發送SYN報文,所以也隨即它會進入到了SYN_SENT狀態,並等待服務端的發送三次握手中的第2個報文。SYN_SENT狀態表示客戶端已發送SYN報文。 `ESTABLISHED`:這個容易理解了,表示鏈接已經創建了. `FIN_WAIT_1`:這個狀態要好好解釋一下,其實FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態其實是當SOCKET在ESTABLISHED狀態時,它想主動關閉鏈接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,固然在實際的正常狀況下,不管對方何種狀況下,都應該立刻迴應ACK報文,因此FIN_WAIT_1狀態通常是比較難見到的,而FIN_WAIT_2狀態還有時經常能夠用netstat看到。 `FIN_WAIT_2`: 上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半鏈接,也即有一方要求close鏈接,但另外還告訴對方,我暫時還有點數據須要傳送給你,稍後再關閉鏈接。 `TIME_WAIT`:表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL後便可回到CLOSED可用狀態了。若是FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,能夠直接進入到TIME_WAIT狀態,而無須通過FIN_WAIT_2狀態。 `CLOSING`:這種狀態比較特殊,實際狀況中應該是不多見,屬於一種比較罕見的例外狀態。正常狀況下,當你發送FIN報文後,按理來講是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。可是CLOSING狀態表示你發送FIN報文後,並無收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼狀況下會出現此種狀況呢?其實細想一下,也不可貴出結論:那就是若是雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的狀況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET鏈接。 `CLOSE_WAIT`: 這種狀態的含義實際上是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給本身,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正須要考慮的事情是察看你是否還有數據發送給對方,若是沒有的話,那麼你也就能夠close這個SOCKET,發送FIN報文給對方,也即關閉鏈接。因此你在CLOSE_WAIT狀態下,須要完成的事情是等待你去關閉鏈接。 `LAST_ACK`:這個狀態仍是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST->ACK->CLOSED
TIME_WAIT狀態也稱爲2MSL等待狀態。每一個具體TCP實現必須選擇一個報文段最大生存時間MSL(Maximum Segment Lifetime)。它是任何報文段被丟棄前在網絡內的最長時間。處理原則:當TCP執行一個主動關閉,併發回最後一個ACK,該連接必須在TIME_WAIT狀態停留的時間爲2MSL。這樣可以讓TCP有機會在此發送最後一個ACK以防這個ACK丟失(在另外一端發送FIN前提)
可是,在鏈接處於2MSL等待時,任何遲到返回的報文段將被丟棄。由於處於2MSL等待的、由該插口對(socket pair)定義的鏈接在這段時間內不能被再用,對於客戶程序還好 一些,可是對於服務程序,例如httpd,它老是要使用同一個端口80來進行服務,而在 2MSL時間內,啓動httpd就會出現錯誤(插口被使用)。爲了不這個錯誤,服務器給出了一個平靜時間(quit time)的概念,這是說在2MSL時間內,雖然能夠從新啓動服務器,可是這個服務器仍是要平靜等待MSL時間才能進行下一次鏈接,讓後來返回的數據包沒有機會影響到發送端,由於返回的包和從新創建的包使用同一個四元組,發送端沒法區分這兩個包屬於不一樣鏈接。(建議MSL時間爲2min,不過這個與操做系統有關。)
若是以防已經關閉或異常終止鏈接而另外一方殊不知道,咱們將這樣的TCP鏈接稱爲半打開的。這種狀態能夠經過Keepalive選項來進行發現兩一段已經消失。還有一種形式是:本端發送SYN,對端迴應ACK+SYN,此時本段不迴應ACK。
**當處於半打開狀態的一方重啓並從新鏈接後,它將丟失復位前的全部信息,所以它並不知道數據報文段中提到的鏈接。此時就會返回RST(異常終止要發送RST置位的包)包應答,已關閉這次鏈接。此時只須要等待MSL時間,由於TCP默認機器重啓的時間大於MSL。PIX防火牆和IDS*檢測系統均可以假裝×××目標發送RST的包去終止異常的TCP鏈接。(好比限定鏈接的時間,減小半開鏈接限制超時時間)當咱們Telnet一個不存在的端口號時,本段立馬收到一個拒絕訪問的包,這個就是對方發送的RST包致使的。
單方向鏈路關閉。即TCP鏈接一端在結束它的發送後還能接收來自另外一端數據的能力。程序調用的是shutdown,而不是close,不過大多數程序都是調用close終止兩個方向的鏈接。
最大報文段長度表示TCP傳往另外一端的最大塊數據的長度。當創建一個鏈接時,每一方都受到對方通告的MSS值(MSS選項只能出如今SYN報文中)。若是一方收不到另外一方的MSS值,那麼就設爲默認的536字節。MSS是最長見的選項字段,還有另外一個選項叫作窗口放大因子(Window*Shift Count便可以發送超大的數據包,即乘以目前窗口的倍數爲實際一次發送的數據量。解決高速鏈路和高速主機普通TCP發包過慢問題。)。還有一些HASH值也會放在Option字段。而防火牆默認則會清掉IP和TCP的Option選項字段。