TCP -- Transmission Control Protocol
- 可靠性
- TCP報頭
- 确認應答(ACK)機制
- 逾時重傳
- 連接配接管理機制
-
- TCP狀态轉移圖
- 三次握手
- 四次揮手
-
- TIME_WAIT狀态
- 流量控制
- 擁塞控制
- TCP細節優化
-
- 快重傳
- 延遲應答
- 捎帶應答
- 面向位元組流
- TCP異常情況
TCP協定和UDP協定是運作在傳輸層的協定,也是為了結局程序間通信的問題,它相對于UDP協定而言,是一種有連接配接的,可靠的協定。
可靠性
為什麼需要可靠性?
因為網絡的不可靠,而應用層很多時候需要可靠的傳輸(核心)
什麼是可靠性?
- 盡可能的吧資料發送給對方,不丢包
- 如果實在發送不到,至少通知應用層這個錯誤
- 保證資料的有序到達
- TCP保證不會收的錯誤的資料(隻能保證無意識的錯誤,不能防範有意識的篡改)
TCP報頭
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR1kejpWTwUkaNFDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLkJTMldTZ3AjNyITZ2ITM4UWM4QzYlFmZlNjMyI2YihzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
确認應答(ACK)機制
簡單了解,可以了解為TCP發送的資料是有編号的,當接收方收到資料後,有責任進行應答,将期待下一次收到的資料的第一個編号發送回來
例:
TCP(Segment)既可以當發送使用,也可以當應答使用,互相不沖突
标志位上的ACK就是含有應答的意義(==1是應答,==0不是應答,ASN無效)
逾時重傳
如果長時間未收到應答,可能發生什麼?
- 資料包可能丢了,對方沒有收到。
- 對方應答了,但應答包丢失
如果超過一定的時間,仍未收到對應應答,則将對應的資料重新進行發送
如果是應答包丢失的情況下,再次發送資料過去,接收方是否可以判斷?
可以,TCP内部管理着接收資料的序号,TCP段帶有序号
接下來,接收端會将資料丢棄,然後發送應答
如果逾時重傳後還是沒有收到應答怎麼辦?
多次嘗試(有限次數)
1、多次嘗試(retry)過程中,逾時時間往往越來越長
2、當多次嘗試不成功,達到一定門檻值時,就會停止發送,同時還會發一個Segment的充值連結,同時通知應用層,(java中會收到一個異常——SocketException:reset by remote peer)
資料編号 + 确認應答 + 逾時重傳 可以使得資料有序
連接配接管理機制
為什麼需要建立連接配接?至少能證明對方是線上的。
在正常情況下,TCP都要經過三次握手建立連接配接,四次揮手斷開連接配接
有了連接配接管理後,就隻能一對一通信了,沒辦法廣播了。
TCP狀态轉移圖
三次握手
SYN是用作TCP段同步的意義
首先由主動連接配接方發起一個 SYN,别連接配接方收到會回複一個ACK,然後被連接配接方也會發送一個SYN,連接配接方收到回複ACK
如圖:
也可以了解為:
第一次握手: A給B打電話說,你可以聽到我說話嗎?
第二次握手: B收到了A的資訊,然後對A說: 我可以聽得到你說話啊,你能聽得到我說話嗎?
第三次握手: A收到了B的資訊,然後說可以的,我要給你發資訊啦!
在三次握手之後,A和B都能确定這麼一件事: 我說的話,你能聽到; 你說的話,我也能聽到。 這樣,就可以開始正常通信了。
如果兩次,那麼B無法确定B的資訊A是否能收到,是以如果B先說話,可能後面的A都收不到,會出現問題 。
如果四次,那麼就造成了浪費,因為在三次結束之後,就已經可以保證A可以給B發資訊,A可以收到B的資訊; B可以給A發資訊,B可以收到A的資訊。
四次揮手
當需要釋放連接配接的時候,至少由一方主動發起,
主動關閉方發送FIN,被關閉方發送ACK,被關閉方發送FIN,主動關閉方發送ACK
為什麼不可以三次揮手呢?
關閉連接配接時,當收到對方的FIN封包通知時,它僅僅表示對方沒有資料發送給你了;但未必你所有的資料都全部發送給對方了
是以你未必會馬上關閉SOCKET,也即你可能還需要發送一些資料給對方之後,再發送FIN封包給對方來表示你同意現在可以關閉連接配接了,是以它這裡的ACK封包和FIN封包多數情況下都是分開發送的。
可能有人會有疑問,tcp我握手的時候為何ACK(确認)和SYN(建立連接配接)是一起發送。揮手的時候為什麼是分開的時候發送呢?
因為當Server端收到Client端的SYN連接配接請求封包後,可以直接發送SYN+ACK封包。其中ACK封包是用來應答的,SYN封包是用來同步的。
但是關閉連接配接時,當Server端收到FIN封包時,很可能并不會立即關閉 SOCKET,是以隻能先回複一個ACK封包,告訴Client端,“你發的FIN封包我收到了”。隻有等到我Server端所有的封包都發送完了,我才能發送FIN封包,是以不能一起發送。故需要四步揮手。
簡單了解四次揮手:
A:“喂,我不說了 (FIN)。”A->FIN_WAIT1
B:“我知道了(ACK)。等下,上一句還沒說完。Balabala……(傳輸資料)”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,說完了,我也不說了(FIN)。”B->LAST_ACK
A:”我知道了(ACK)。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保證B收到了消息,否則重說一次”我知道了”,A->CLOSED
這樣,通過四次揮手,可以把該說的話都說完,并且A和B都知道自己沒話說了,對方也沒花說了,然後就挂掉電話(斷開連結)了 。
TIME_WAIT狀态
TIME_WAIT這個狀态出現在主動關閉方,并且持續時間是2 *MSL(Maximum Segment Live)
出現的原因:
- 最後一個ACK可能會丢失,是以需要保持一段時間
- 把停止使用的五元組閑置一段時間,保證網絡上這條連接配接的資料全部失效,小機率可能性這個五元組又重新配置設定了,也保證收到資料一定是新連接配接
- 2 * MSL的時間,可以保證 : 對方大機率收到了我們的ACK,網絡上所有老連接配接的包已經失效了,即使五元組在配置設定,也安全了。
作為開發人員,發現伺服器上出現了大量的TIME_WAIT,是不是合理的呢?對伺服器有沒有影響?如果有影響怎麼調增代碼結構?
理論上說是合理的。
會有一些影響,連接配接對象一直存在,關于對象關聯下的一系列資源都需要保留(記憶體)
解決方法:避免我們主動關閉連結
CLOSE_WAIT狀态。是被關閉方的狀态
一般而言,對于伺服器上出現大量的CLOSE_WAIT,原因是伺服器沒有正确的關閉socket導緻四次揮手沒有正确的完成,隻需要加上對應的close解決問題。
流量控制
根據接收方的接受能力進行發送量控制
接收視窗:接收方告訴發送方自己目前的接收能力
發送視窗:控制發送量
滑動視窗:控制發送量過程中的過程、動作
在TCP首部的視窗大小的字段通過ACK機制通知發送端,
在TCP發送緩沖區:
例如:
擁塞控制
根據網絡擁塞視窗推斷出發送量的大小
根據丢包情況,反推網絡擁塞情況
發送量控制 流量控制 + 擁塞控制
TCP細節優化
快重傳
資料包不停地向接收方發送(不必等到收到ACK在發送下一個),接收方傳遞會ACK應答。如果有一個包丢了,那麼應答ACK會一直發送下一個應該發送的位元組頭,當到達一定次數,發送方會重新發送,丢掉的包,收到後傳回下一個應該發送的位元組頭。
延遲應答
可以每N個包應答一次,超過最大的延遲時間就應答一次
捎帶應答
非sync包,理論上都可以捎帶應答
在延遲應答的基礎上我們發現每次發送都是 一發一收,我怕,我們的ACK可以搭順風車。
面向位元組流
TCP逾時重傳機制 + 發送量控制帶來的副作用是:資料隻能通過位元組流的形式提供給對方
TCP有自己的發送緩沖區和接收緩沖區。
我們要在UDP協定上開發一個可靠的應用層協定,具體怎麼做?
具有TCP的特性:
校驗和、序列号、确認應答、逾時重發、連接配接管理、流量控制、擁塞控制
TCP異常情況
1、程序終止:程序終止會釋放檔案描述符,仍然可以發送FIN,和正常關閉一樣
2、機器重新開機:和程序終止一樣
機器掉電/網線斷開:接收端認為連接配接還在,一旦接收端有寫入操作,接收端發現連接配接不在了,就會進行reset,TCP自己也内置了一個保活定時器,會定期詢問對方是否還在,如果不在也會釋放。
- 隻要os有機會介入,os就有能力,将所有的連接配接進行四次揮手
- 如果是os沒有機會介入的關機,主機B上的程序、連接配接都沒有了
- 如果處于寫資料(發送資料)的狀态,當到達充實門檻值時,會判斷為連接配接已關閉(異常關閉),通知應用層,SocketException,需要一定時間,不能立即可知
-
如果處于讀資料的狀态——确實無法判定
解決問題思路:
1、TCP協定層面有一種KeepAlive機制(每隔一段時間,嘗試發送個空資料)
2、應用層方法:read(timeout),如果逾時了,嘗試寫一個資料過去——>心跳包(heartbeat)