很開心,上半年發佈的spring boot 2中,默認的web 容器是netty ,這說明「反應式」 容器已是大勢所趨,不管是go 語言的協從線程,仍是java 基於reactor 線程模型,都是基於事件編程實現高併發的實例。這周開始我會講關於NiO的一切,底層原理是什麼,應用架構有哪些,如何利用其優點構建高性能服務器,歡迎關注。java
在介紹NIO以前有必要了解下TCP協議,由於目前多數應用都是給予應用層進行操做,致使隱藏了大量的網路細節,知道這些細節以及原理對咱們的問題排查頗有益處。react
TCP 是一種面向鏈接的協議,它給用戶進程提供可靠的全雙工的字節流。確保數據包的可靠,有序,以及支持流量控制。關於TCP 爲什麼要作這些,咱們從如下幾個方面入手:web
IP網絡層爲什麼不保證數據包的可靠性算法
TCP協議如何保證包可達、有序spring
TCP協議如何支持流量控制編程
TCP幾種狀態以及應用緩存
咱們先看下OSI的網絡分層,在如下分層中,TCP 位於傳輸層,它保證的是協議的可靠性和連續性。具體的收發報是有底層的鏈路層以及物理層所決定的,因此TCP 所作的工做,也是基於底層的優化和改進。bash
客戶端與服務器之間的通訊使用應用協議,傳輸層的通訊採用TCP協議,而TCP 協議又採用了更低的一層IP協議,IP則使用某種形式的數據鏈路層通訊。服務器
咱們知道網絡的中的數據,最終經過多個路由器鏈接傳送的。最底層的以太網協議規定了電子信號如何組成數據包,解決了局域網的點對點通訊問題,但沒法解決多個局域網的的互通問題。網絡
而網絡層採用的IP協議,就是定義了一套本身的地址規則,主要解決尋址和路由的功能,根據對方的IP地址,尋找最佳路徑傳輸信息。局域網經過路由器鏈接,路由器基於IP協議,指導數據包向某個路由藉口轉發。但IP協議不保證包必定到達以及完整性,特別是網絡擁堵的時候,會丟棄一些數據包,保證數據的傳送效率。
而保證數據包的完整、有序以及可靠,這就是TCP 協議要來作的事情了。
不少網絡有一個最大傳送單元,它是鏈路層中的網絡對數據幀的一個限制,以以太網爲例,MTU爲1500個字節。一個IP數據報在以太網中 傳輸,若是它的長度大於該MTU值,就要進行分片傳輸,使得每片數據報的長度小於MTU。
另一個數據包還包含頭信息,除了本身的Tcp包頭,還有IP 頭信息和以太網頭信息。IP 數據包在以太網數據包的負載裏面,最少須要20字節,因此 IP 數據包的負載最多爲1480字節。
那麼tcp的一個包大小是多少吶?
咱們須要機遇MSS這個值來肯定,MSS是TCP裏的一個概念(首部的選項字段中)。MSS是TCP數據包每次可以傳輸的最大數據分段,TCP報文段的長度大於MSS時,要進行分段傳輸。 若是不設置,則MSS的默認值就爲536個字節 。也就是說一個tcp包的在500字節左右。
上述也說了,底層的路由轉發包,並不保證包的可靠性以及有序性。
首先爲了保證包的完整性,TCP 會基於MSS 爲大於 MSS的包進行分包處理,默認MSS大小爲563byte,其大小小於MUT,以防止在網絡層被分片處理。
其次增長SEQ和ACK,同時採用超時重發的機制來保證包的可靠性。
爲了保證有序性,TCP 爲每一個包編配一個Sequence number ,簡稱 SEQ 。以便接收的一方按照順序還原。萬一發生丟包,也能夠知道丟失的是哪個包。通常第一個包的編號是一個隨機數,也能夠從1開始。
那麼有編號了,如何確保包必定到達?
基於ACK 進行確認。對於接收方來講,每次接受一個包必須返回ack信息,發送端從而確認這個包已經傳送到。另外,接收方要對每一條報文作校驗。若是校驗發現出錯,則不發送確認報文,從而觸發發送方超時重傳。
ACK 包含如下信息:
期待要收到下一個數據包的編號 next SEQ
接收方的接收窗口的剩餘容量
咱們採用wiershark抓包一個oschina的包看下三次握手的數據。
個人本機ip:192.168.1.103
oschinaIp:116.211.174.177
三次握手過程:
1.me->osChina:syn=1 seq=x ack=0
2.osChina->me:syn=1 seq=y ack=x+1
3.me->osChina:seq=x+1 ack=y+1複製代碼
一、me->osChina:syn=1 seq=0 ack=0
二、osChina->me:syn=1 seq=0 ack=0+1
三、me->osChina:seq=0+1 ack=0+1
對比一下三次握手的過程。
咱們知道網絡極其不穩定,數據包即使增長了SEQ和ACK,可以保證其有序性,但依然保證丟包或者超時的問題。若是發送端發送數據,或者接收端回覆ACK的消息在網絡中丟失或者超時怎麼處理?
RTO ,超時重傳時間。要知道包是否出現超時,須要有一個評估方式,而RTT是對一個給定鏈接的往返時間的測量。因爲網絡流量的變化,這個時間會相應地發生改變,TCP須要跟蹤這些變化並動態調整超時時間RTO。
發送方若是必定時間內沒收到報文的ACK,就認爲該報文丟失在網絡中了,自動重發該報文。這種機制稱之爲超時重傳。
在這期間,若是接收端的消息,因爲丟失,接收端沒有收到ack 消息,發送端會向接收端重發這個包。若是由於超時緣由,發送端在超時定時器以後收到了這個包的ack 信息,並且發送端已經重複發送了這個消息,此時發送端不會處理,直接丟棄該ack 。而接收端接收到了以後會再次回覆ack 信息。
上述中咱們知道了TCP協議能夠保證數據的可靠性,可是也得兼顧效率。兼顧效率的話須要考慮如下三個方面:
支持批量發包
可以基於網絡的情況,支持擁堵控制
可以瞭解接收端的情況,防止接收端處理不過來
基於以上三個需求,作了如下處理。
若是TCP 中的包,都須要發送一個確認一個的話,效率過低了,單次發送和確認一個包,雖然保證了可靠性,但沒法保證其效率。此時須要一個批量發送和確認的方式,這就是滑動窗口所作的事情。
發送滑動窗口:
發送窗口從左向右移動在這個發送窗口以前的數據必然是已經發送並且獲得接收方確認的數據落在發送窗口以內的數據是發送方能夠發送的數據在發送窗口以後的數據是不能發送的數據。
若是發生超時或者丟失現象。那麼有兩種解決方案:
一、回退N,丟失的包號以後全部包都重發二、選擇重傳ARQ,只發丟失的,避免重複的(效率高,防止發送重複的)
滑動窗口還有一個做用是讓發送端知道接收端的處理情況。假設TCP接收方的緩存已經滿了,沒法處理更多的,而發送方並不知道,每次會給對方告知當前滑動窗口的大小值 ,此時發送端就不會再發送數據了。
接收方接收到數據一樣立刻發送確認,可是同時對發送方宣佈窗口大小爲0。這樣發送方就暫時不會發送數據。
報文到達時不立刻發送確認,直到緩存有足夠的空間。這樣就能夠避免發送方滑動窗口。可是這也存在一個問題,接收方延遲發送確認的時間不該該超過超時時間,若是過長會致使發送方誤覺得數據丟失從新發送數據。
咱們知道網絡情況有好友壞,好的時候,能夠多發些包,壞的時候,若是發包速率不變的話,除了會加劇網路負擔之外,還會形成包的過多丟失,除非更多的超時重發,這無疑識下降了通訊效率。
基於此,TCP通訊雙方維護一個叫作擁塞窗口(cwnd,congesion window)的值,這個值取決於網絡中的擁塞率,發送方的發送窗口的值就等於擁塞窗口的大小。只要網絡中沒有出現擁塞,擁塞窗口的值就能夠增大一些,這樣發送方能夠發送到網絡中的數據就多一些。反之,擁塞窗口的值就減少,從而避免加重網絡的擁塞率。
TCP目前擁塞控制主要有如下4種算法:
慢啓動
擁塞避免
快速重傳
快恢復
具體的算法實現方式就再也不介紹了,大概實現的功能就是,基於當前的網絡情況,找到一個合適的發送速率,防止給網絡形成過大的負擔。好比說慢啓動,就是開始的時候,發送得較慢,而後根據丟包的狀況,調整速率:若是不丟包,就加快發送速度;若是丟包,就下降發送速度。
瞭解TCP的都知道,TCP 創建鏈接的時候,有三次握手,斷開連接的時候又四次握手交互。那麼其中的狀態是有哪些?
上面的圖看着是否是太亂記不住,咱們看看下面這張梳理一下,看看具體應用狀態。
從上面能夠看到,鏈接創建成功的時候,其狀態是ESTABLISHED 的。當接受端的狀態爲SYN—RECV的時候,表示接受端,已經回覆第二次握手信息了,等待發送端再次確認。若是網絡中遭受到大量的SYN 攻擊,會存在大量的SYN_RECV 狀態。此時能夠定位這些問題IP ,經過防火牆過濾就能解決大量的假鏈接問題。
在網絡中,某一端主動關閉而沒有經過四次握手關閉,此時tcp已經創建的通道是否還在,多久會關閉?此時的TCP 狀態爲TIME_WAIT ,能夠想象,現實中常常出現這種情況,多數的關閉鏈接都是主動關閉而非經過協商通訊關閉。那麼此時關閉,若果再重連還能重連上以前的tcp 通道麼,仍是須要重現建立。
任何TCP實現必須爲MSL選擇一個值,默認是2分鐘或者30秒,TIME_WAIT默認是2倍的MSL,持續時間在1-4分鐘之間。MSL是IP數據包能在網絡中存活的最長時間。
TIME_WAIT 存在的兩個理由: 一、可靠的實現TCP全雙工鏈接的終止 二、容許老的重複分節在網絡中消失
TCP必須防止某個鏈接的老的重複分組在該鏈接已經終止後再現,從而被誤解成屬於同一鏈接的化身,有time_wait 足夠長,是2倍的MSL的,那麼足夠讓某個方向上的分組最多存活MSL秒就被丟棄。
從TIME_WAIT狀態到CLOSED狀態,有一個超時設置,這個超時設置是 2*MSL(RFC793定義了MSL爲2分鐘,Linux設置成了30s),如此超過了這個時間,當前的tcp通道就會被定義爲關閉。
更多架構知識,歡迎關注個人公衆號,大碼候(cool_wier)