文章很長,建議收藏起來,慢慢讀! 瘋狂創客圈爲小夥伴奉上如下珍貴的學習資源:html
高併發 必讀 的精彩博文 | |
---|---|
nacos 實戰(史上最全) | sentinel (史上最全+入門教程) |
Zookeeper 分佈式鎖 (圖解+秒懂+史上最全) | Webflux(史上最全) |
SpringCloud gateway (史上最全) | TCP/IP(圖解+秒懂+史上最全) |
10分鐘看懂, Java NIO 底層原理 | Feign原理 (圖解) |
更多精彩博文 ..... | 請參見【 瘋狂創客圈 高併發 總目錄 】 |
TCP/IP協議包含了一系列的協議,也叫TCP/IP協議族(TCP/IP Protocol Suite,或TCP/IP
Protocols),簡稱TCP/IP。TCP/IP協議族提供了點對點的連結機制,而且將傳輸數據幀的封裝、尋址、傳輸、路由以及接收方式,都予以標準化。java
在展開介紹TCP/IP協議以前,首先介紹一下七層ISO模型。國際標準化組織ISO爲了使網絡應用更爲普及,推出了OSI參考模型,即開放式系統互聯(Open
System Interconnect)模型,
通常都叫OSI參考模型。OSI參考模型是ISO組織在1985年發佈的網絡互連模型,其含義就是爲全部公司使用一個統一的規範來控制網絡,這樣全部公司遵循相同的通訊規範,網絡就能互聯互通了。程序員
OSI模型定義了網絡互連的七層框架(物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層),每一層實現各自的功能和協議,並完成與相鄰層的接口通訊。OSI模型各層的通訊協議,大體舉例以下表所示:面試
表:OSI模型各層的通訊協議舉例算法
應用層 | HTTP、SMTP、SNMP、FTP、Telnet、SIP、SSH、NFS、RTSP、XMPP、Whois、ENRP、等等 |
---|---|
表示層 | XDR、ASN.一、SMB、AFP、NCP、等等 |
會話層 | ASAP、SSH、RPC、NetBIOS、ASP、Winsock、BSD Sockets、等等 |
傳輸層 | TCP、UDP、TLS、RTP、SCTP、SPX、ATP、IL、等等 |
網絡層 | IP、ICMP、IGMP、IPX、BGP、OSPF、RIP、IGRP、EIGRP、ARP、RARP、X.2五、等等 |
數據鏈路層 | 以太網、令牌環、HDLC、幀中繼、ISDN、ATM、IEEE 802.十一、FDDI、PPP、等等 |
物理層 | 例如銅纜、網線、光纜、無線電等等 |
TCP/IP協議是Internet互聯網最基本的協議,其在必定程度上參考了七層ISO模型。OSI模型共有七層,從下到上分別是物理層、數據鏈路層、網絡層、運輸層、會話層、表示層和應用層。可是這顯然是有些複雜的,因此在TCP/IP協議中,七層被簡化爲了四個層次。TCP/IP模型中的各類協議,依其功能不一樣,被分別歸屬到這四層之中,常被視爲是簡化事後的七層OSI模型。sql
TCP/IP協議與七層ISO模型的對應關係,大體以下圖所示:
編程
圖:TCP/IP協議與七層ISO模型的對應關係設計模式
TCP/IP協議的應用層的主要協議有HTTP、Telnet、FTP、SMTP等,是用來讀取來自傳輸層的數據或者將數據傳輸寫入傳輸層;傳輸層的主要協議有UDP、TCP,實現端對端的數據傳輸;網絡層的主要協議有ICMP、IP、IGMP,主要負責網絡中數據包的傳送等;鏈路層有時也稱做數據鏈路層或網絡接口層,主要協議有ARP、RARP,
一般包括操做系統中的設備驅動程序和計算機中對應的網絡接口卡,它們一塊兒處理與傳輸媒介(如電纜或其餘物理設備)的物理接口細節。瀏覽器
(一)TCP/IP協議的應用層緩存
應用層包括全部和應用程序協同工做,並利用基礎網絡交換應用程序的業務數據的協議。一些特定的程序被認爲運行在這個層上,該層協議所提供的服務能直接支持用戶應用。應用層協議包括HTTP(萬維網服務)、FTP(文件傳輸)、SMTP(電子郵件)、SSH(安全遠程登錄)、DNS(域名解析)以及許多其餘協議。
(二)TCP/IP協議的傳輸層
傳輸層的協議,解決了諸如端到端可靠性問題,能確保數據可靠的到達目的地,甚至能保證數據按照正確的順序到達目的地。傳輸層的主要功能大體以下:
(1)爲端到端鏈接提供傳輸服務;
(2)這種傳輸服務分爲可靠和不可靠的,其中TCP是典型的可靠傳輸,而UDP則是不可靠傳輸;
(3)爲端到端鏈接提供流量控制、差錯控制、QoS(Quality of
Service)服務質量等管理服務。
傳輸層主要有兩個性質不一樣的協議:TCP傳輸控制協議和UDP用戶數據報協議。
TCP協議是一個面向鏈接的、可靠的傳輸協議,它提供一種可靠的字節流,能保證數據完整、無損而且按順序到達。TCP儘可能接二連三地測試網絡的負載而且控制發送數據的速度以免網絡過載。另外,TCP試圖將數據按照規定的順序發送。
UDP協議是一個無鏈接的數據報協議,是一個「盡力傳遞」和「不可靠」協議,不會對數據包是否已經到達目的地進行檢查,而且不保證數據包按順序到達。
整體來講,TCP協議傳輸效率低,但可靠性強;UDP協議傳輸效率高,但可靠性略低,適用於傳輸可靠性要求不高、體量小的數據(好比QQ聊天數據)。
(三)TCP/IP協議的網絡層
TCP/IP協議網絡層的做用是在複雜的網絡環境中爲要發送的數據報找到一個合適的路徑進行傳輸。簡單來講,網絡層負責將數據傳輸到目標地址,目標地址能夠是多個網絡經過路由器鏈接而成的某一個地址。另外,網絡層負責尋找合適的路徑到達對方計算機,並把數據幀傳送給對方,網絡層還能夠實現擁塞控制、網際互連等功能。網絡層協議的表明包括:ICMP、IP、IGMP等。
(四)TCP/IP協議的鏈路層
鏈路層有時也稱做數據鏈路層或網絡接口層,用來處理鏈接網絡的硬件部分。該層既包括操做系統硬件的設備驅動、NIC(網卡)、光纖等物理可見部分,還包括鏈接器等一切傳輸媒介。在這一層,數據的傳輸單位爲比特。其主要協議有ARP、RARP等。
好久好久以前,你不與任何其餘電腦相鏈接,孤苦伶仃。
直到有一天,你但願與另外一臺電腦 B 創建通訊,因而大家各開了一個網口,用一根網線鏈接了起來。
用一根網線鏈接起來怎麼就能"通訊"了呢?我能夠給你講 IO、講中斷、講緩衝區,但這不是研究網絡時該關心的問題。
若是你糾結,要麼去研究一下操做系統是如何處理網絡 IO 的,要麼去研究一下包是如何被網卡轉換成電信號發送出去的,要麼就僅僅把它當作電腦裏有個小人在開槍吧~
反正,大家就是連起來了,而且能夠通訊。
有一天,一個新夥伴 C 加入了,但聰明的大家很快發現,能夠每一個人開兩個網口,用一共三根網線,彼此相連。
隨着愈來愈多的人加入,你發現身上開的網口實在太多了,並且網線密密麻麻,混亂不堪。(而實際上一臺電腦根本開不了這麼多網口,因此這種連線只在理論上可行,因此連不上的我就用紅色虛線表示了,就是這麼嚴謹哈哈~)
因而大家發明了一箇中間設備,大家將網線都插到這個設備上,由這個設備作轉發,就能夠彼此之間通訊了,本質上和原來同樣,只不過網口的數量和網線的數量減小了,再也不那麼混亂。
你給它取名叫集線器,它僅僅是無腦將電信號轉發到全部出口(廣播),不作任何處理,你以爲它是沒有智商的,所以把人家定性在了物理層。
因爲轉發到了全部出口,那 BCDE 四臺機器怎麼知道數據包是否是發給本身的呢?
首先,你要給全部的鏈接到交換機的設備,都起個名字。原來大家叫 ABCD,但如今須要一個更專業的,全局惟一的名字做爲標識,你把這個更高端的名字稱爲 MAC 地址。
你的 MAC 地址是 aa-aa-aa-aa-aa-aa,你的夥伴 b 的 MAC 地址是 bb-bb-bb-bb-bb-bb,以此類推,不重複就好。
這樣,A 在發送數據包給 B 時,只要在頭部拼接一個這樣結構的數據,就能夠了。
B 在收到數據包後,根據頭部的目標 MAC 地址信息,判斷這個數據包的確是發給本身的,因而便收下。
其餘的 CDE 收到數據包後,根據頭部的目標 MAC 地址信息,判斷這個數據包並非發給本身的,因而便丟棄。
雖然集線器使整個佈局乾淨很多,但原來我只要發給電腦 B 的消息,如今卻要發給鏈接到集線器中的全部電腦,這樣既不安全,又不節省網絡資源。
若是把這個集線器弄得更智能一些,只發給目標 MAC 地址指向的那臺電腦,就行了。
雖然只比集線器多了這一點點區別,但看起來彷佛有智能了,你把這東西叫作交換機。也正由於這一點點智能,你把它放在了另外一個層級,數據鏈路層。
如上圖所示,你是這樣設計的。
交換機內部維護一張 MAC 地址表,記錄着每個 MAC 地址的設備,鏈接在其哪個端口上。
MAC 地址 | 端口 |
---|---|
bb-bb-bb-bb-bb-bb | 1 |
cc-cc-cc-cc-cc-cc | 3 |
aa-aa-aa-aa-aa-aa | 4 |
dd-dd-dd-dd-dd-dd | 5 |
假如你仍然要發給 B 一個數據包,構造了以下的數據結構從網口出去。
到達交換機時,交換機內部經過本身維護的 MAC 地址表,發現目標機器 B 的 MAC 地址 bb-bb-bb-bb-bb-bb 映射到了端口 1 上,因而把數據從 1 號端口發給了 B,完事~
你給這個經過這樣傳輸方式而組成的小範圍的網絡,叫作以太網。
固然最開始的時候,MAC 地址表是空的,是怎麼逐步創建起來的呢?
假如在 MAC 地址表爲空是,你給 B 發送了以下數據
因爲這個包從端口 4 進入的交換機,因此此時交換機就能夠在 MAC地址表記錄第一條數據:
MAC:aa-aa-aa-aa-aa-aa-aa
端口:4
交換機看目標 MAC 地址(bb-bb-bb-bb-bb-bb)在地址表中並無映射關係,因而將此包發給了全部端口,也即發給了全部機器。
以後,只有機器 B 收到了確實是發給本身的包,因而作出了響應,響應數據從端口 1 進入交換機,因而交換機此時在地址表中更新了第二條數據:
MAC:bb-bb-bb-bb-bb-bb
端口:1
過程以下
通過該網絡中的機器不斷地通訊,交換機最終將 MAC 地址表創建完畢~
隨着機器數量越多,交換機的端口也不夠了,但聰明的你發現,只要將多個交換機鏈接起來,這個問題就垂手可得搞定~
你徹底不須要設計額外的東西,只須要按照以前的設計和規矩來,按照上述的接線方式便可完成全部電腦的互聯,因此交換機設計的這種規則,真的很巧妙。你想一想看爲何(好比 A 要發數據給 F)。
可是你要注意,上面那根紅色的線,最終在 MAC 地址表中可不是一條記錄呀,而是要把 EFGH 這四臺機器與該端口(端口6)的映射所有記錄在表中。
最終,兩個交換機將分別記錄 A ~ H 全部機器的映射記錄。
左邊的交換機
MAC 地址 | 端口 |
---|---|
bb-bb-bb-bb-bb-bb | 1 |
cc-cc-cc-cc-cc-cc | 3 |
aa-aa-aa-aa-aa-aa | 4 |
dd-dd-dd-dd-dd-dd | 5 |
ee-ee-ee-ee-ee-ee | 6 |
ff-ff-ff-ff-ff-ff | 6 |
gg-gg-gg-gg-gg-gg | 6 |
hh-hh-hh-hh-hh-hh | 6 |
右邊的交換機
MAC 地址 | 端口 |
---|---|
bb-bb-bb-bb-bb-bb | 1 |
cc-cc-cc-cc-cc-cc | 1 |
aa-aa-aa-aa-aa-aa | 1 |
dd-dd-dd-dd-dd-dd | 1 |
ee-ee-ee-ee-ee-ee | 2 |
ff-ff-ff-ff-ff-ff | 3 |
gg-gg-gg-gg-gg-gg | 4 |
hh-hh-hh-hh-hh-hh | 6 |
這在只有 8 臺電腦的時候還好,甚至在只有幾百臺電腦的時候,都還好,因此這種交換機的設計方式,已經足足支撐一陣子了。
但很遺憾,人是貪婪的動物,很快,電腦的數量就發展到幾千、幾萬、幾十萬。
交換機已經沒法記錄如此龐大的映射關係了。
此時你動了歪腦筋,你發現了問題的根本在於,連出去的那根紅色的網線,後面不知道有多少個設備不斷地鏈接進來,從而使得地址表愈來愈大。
那我可不可讓那根紅色的網線,接入一個新的設備,這個設備就跟電腦同樣有本身獨立的 MAC 地址,並且同時還能幫我把數據包作一次轉發呢?
這個設備就是路由器,它的功能就是,做爲一臺獨立的擁有 MAC 地址的設備,而且能夠幫我把數據包作一次轉發,你把它定在了網絡層。
注意,路由器的每個端口,都有獨立的 MAC 地址
好了,如今交換機的 MAC 地址表中,只須要多出一條 MAC 地址 ABAB 與其端口的映射關係,就能夠成功把數據包轉交給路由器了,這條搞定。
那如何作到,把發送給 C 和 D,甚至是把發送給 DEFGH.... 的數據包,通通先發送給路由器呢?
不難想到這樣一個點子,假如電腦 C 和 D 的 MAC 地址擁有共同的前綴,好比分別是
C 的 MAC 地址:FFFF-FFFF-CCCC D 的 MAC 地址:FFFF-FFFF-DDDD
那咱們就能夠說,將目標 MAC 地址爲 FFFF-FFFF-?開頭的,通通先發送給路由器。
這樣是否可行呢?答案是否認的。
咱們先從現實中 MAC 地址的結構入手,MAC地址也叫物理地址、硬件地址,長度爲 48 位,通常這樣來表示
00-16-EA-AE-3C-40
它是由網絡設備製造商生產時燒錄在網卡的EPROM(一種閃存芯片,一般能夠經過程序擦寫)。
其中前 24 位(00-16-EA)表明網絡硬件製造商的編號,後 24 位(AE-3C-40)是該廠家本身分配的,通常表示系列號。
只要不更改本身的 MAC 地址,MAC 地址在世界是惟一的。形象地說,MAC地址就如同身份證上的身份證號碼,具備惟一性。
那若是你但願向上面那樣表示將目標 MAC 地址爲 FFFF-FFFF-?開頭的,統一從路由器出去發給某一羣設備(後面會提到這實際上是子網的概念),那你就須要要求某一子網下通通買一個廠商製造的設備,要麼你就須要要求廠商在生產網絡設備燒錄 MAC 地址時,提早按照你規劃好的子網結構來定 MAC 地址,而且往後這個網絡的結構都不能輕易改變。
這顯然是不現實的。
因而你發明了一個新的地址,給每一臺機器一個 32 位的編號,如:
11000000101010000000000000000001
你以爲有些不清晰,因而把它分紅四個部分,中間用點相連。
11000000.10101000.00000000.00000001
你還以爲不清晰,因而把它轉換成 10 進制。
192.168.0.1
最後你給了這個地址一個響亮的名字,IP 地址。如今每一臺電腦,同時有本身的 MAC 地址,又有本身的 IP 地址,只不過 IP 地址是軟件層面上的,能夠隨時修改,MAC 地址通常是沒法修改的。
這樣一個能夠隨時修改的 IP 地址,就能夠根據你規劃的網絡拓撲結構,來調整了。
如上圖所示,假如我想要發送數據包給 ABCD 其中一臺設備,不論哪一臺,我均可以這樣描述,"將 IP 地址爲 192.168.0 開頭的所有發送給到路由器,以後再怎麼轉發,交給它!",巧妙吧。
路由器誕生了,專門負責IP地址的尋找。那報文交給路由器以後,路由器又是怎麼把數據包準確轉發給指定設備的呢?
別急咱們慢慢來。
咱們先給上面的組網方式中的每一臺設備,加上本身的 IP 地址
如今兩個設備之間傳輸,除了加上數據鏈路層的頭部以外,還要再增長一個網絡層的頭部。
假如 A 給 B 發送數據,因爲它們直接連着交換機,因此 A 直接發出以下數據包便可,其實網絡層沒有體現出做用。
但假如 A 給 C 發送數據,A 就須要先轉交給路由器,而後再由路由器轉交給 C。因爲最底層的傳輸仍然須要依賴以太網,因此數據包是分紅兩段的。
A ~ 路由器這段的包以下:
路由器到 C 這段的包以下:
好了,上面說的兩種狀況(A->B,A->C),相信細心的讀者應該會有很多疑問,下面咱們一個個來展開。
A 給 C 發數據包,怎麼知道是否要經過路由器轉發呢?
答案:子網
若是源 IP 與目的 IP 處於一個子網,直接將包經過交換機發出去。
若是源 IP 與目的 IP 不處於一個子網,就交給路由器去處理。
好,那如今只須要解決,什麼叫處於一個子網就行了。
192.168.0.1 和 192.168.0.2 處於同一個子網
192.168.0.1 和 192.168.1.1 處於不一樣子網
這兩個是咱們人爲規定的,即咱們想表示,對於 192.168.0.1 來講:
192.168.0.xxx 開頭的,就算是在一個子網,不然就是在不一樣的子網。
那對於計算機來講,怎麼表達這個意思呢?因而人們發明了子網掩碼的概念
假如某臺機器的子網掩碼定爲 255.255.255.0
這表示,將源 IP 與目的 IP 分別同這個子網掩碼進行與運算****,相等則是在一個子網,不相等就是在不一樣子網,就這麼簡單。
好比
A電腦:192.168.0.1 & 255.255.255.0 = 192.168.0.0
B電腦:192.168.0.2 & 255.255.255.0 = 192.168.0.0
C電腦:192.168.1.1 & 255.255.255.0 = 192.168.1.0
D電腦:192.168.1.2 & 255.255.255.0 = 192.168.1.0
那麼 A 與 B 在同一個子網,C 與 D 在同一個子網,可是 A 與 C 就不在同一個子網,與 D 也不在同一個子網,以此類推。
因此若是 A 給 C 發消息,A 和 C 的 IP 地址分別 & A 機器配置的子網掩碼,發現不相等,則 A 認爲 C 和本身不在同一個子網,因而把包發給路由器,就無論了,以後怎麼轉發,A 不關心。
A 如何知道,哪一個設備是路由器?
答案:在 A 上要設置默認網關
上一步 A 經過是否與 C 在同一個子網內,判斷出本身應該把包發給路由器,那路由器的 IP 是多少呢?
其實說發給路由器不許確,應該說 A 會把包發給默認網關。
對 A 來講,A 只能直接把包發給同處於一個子網下的某個 IP 上,因此發給路由器仍是發給某個電腦,對 A 來講也不關心,只要這個設備有個 IP 地址就行。
因此默認網關,就是 A 在本身電腦裏配置的一個 IP 地址,以便在發給不一樣子網的機器時,發給這個 IP 地址。
僅此而已!
路由器如何知道C在哪裏?
答案:路由表
如今 A 要給 C 發數據包,已經能夠成功發到路由器這裏了,最後一個問題就是,路由器怎麼知道,收到的這個數據包,該從本身的哪一個端口出去,才能直接(或間接)地最終到達目的地 C 呢。
路由器收到的數據包有目的 IP 也就是 C 的 IP 地址,須要轉化成從本身的哪一個端口出去,很容易想到,應該有個表,就像 MAC 地址表同樣。
這個表就叫路由表。
至於這個路由表是怎麼出來的,有不少路由算法,本文不展開,由於我也不會哈哈~
不一樣於 MAC 地址表的是,路由表並非一對一這種明確關係,咱們下面看一個路由表的結構。
目的地址 | 子網掩碼 | 下一跳 | 端口 |
---|---|---|---|
192.168.0.0 | 255.255.255.0 | 0 | |
192.168.0.254 | 255.255.255.255 | 0 | |
192.168.1.0 | 255.255.255.0 | 1 | |
192.168.1.254 | 255.255.255.255 | 1 |
咱們學習一種新的表示方法,因爲子網掩碼其實就表示前多少位表示子網的網段,因此如 192.168.0.0(255.255.255.0) 也能夠簡寫爲 192.168.0.0/24
目的地址 | 下一跳 | 端口 |
---|---|---|
192.168.0.0/24 | 0 | |
192.168.0.254/32 | 0 | |
192.168.1.0/24 | 1 | |
192.168.1.254/32 | 1 |
這就很好理解了,路由表就表示,192.168.0.xxx 這個子網下的,都轉發到 0 號端口,192.168.1.xxx 這個子網下的,都轉發到 1 號端口。下一跳列尚未值,咱們先無論
配合着結構圖來看(這裏把子網掩碼和默認網關都補齊了)
剛纔說的都是 IP 層,但發送數據包的數據鏈路層須要知道 MAC 地址,但是我只知道 IP 地址該怎麼辦呢?
答案:arp
假如你(A)此時不知道你同伴 B 的 MAC 地址(現實中就是不知道的,剛剛咱們只是假設已知),你只知道它的 IP 地址,你該怎麼把數據包準確傳給 B 呢?
答案很簡單,在網絡層,我須要把 IP 地址對應的 MAC 地址找到,也就是經過某種方式,找到 192.168.0.2 對應的 MAC 地址 BBBB。
這種方式就是 arp 協議,同時電腦 A 和 B 裏面也會有一張 arp 緩存表,表中記錄着 IP 與 MAC 地址的對應關係。
IP 地址 | MAC 地址 |
---|---|
192.168.0.2 | BBBB |
一開始的時候這個表是空的,電腦 A 爲了知道電腦 B(192.168.0.2)的 MAC 地址,將會廣播一條 arp 請求,B 收到請求後,帶上本身的 MAC 地址給 A 一個響應。此時 A 便更新了本身的 arp 表。
這樣經過你們不斷廣播 arp 請求,最終全部電腦裏面都將 arp 緩存表更新完整。
從各個節點的視角來看
首先我要知道個人 IP 以及對方的 IP
經過子網掩碼判斷咱們是否在同一個子網
在同一個子網就經過 arp 獲取對方 mac 地址直接扔出去
不在同一個子網就經過 arp 獲取默認網關的 mac 地址直接扔出去
我收到的數據包必須有目標 MAC 地址
經過 MAC 地址表查映射關係
查到了就按照映射關係從個人指定端口發出去
查不到就全部端口都發出去
路由器視角:
我收到的數據包必須有目標 IP 地址
經過路由表查映射關係
查到了就按照映射關係從個人指定端口發出去(不在任何一個子網範圍,走其路由器的默認網關也是查到了)
查不到則返回一個路由不可達的數據包
若是你嗅覺足夠敏銳,你應該能夠感覺到下面這句話:
網絡層(IP協議)自己沒有傳輸包的功能,包的實際傳輸是委託給數據鏈路層(以太網中的交換機)來實現的。
涉及到的三張表分別是
交換機中有 MAC 地址表用於映射 MAC 地址和它的端口
路由器中有路由表用於映射 IP 地址(段)和它的端口
電腦和路由器中都有** arp 緩存表**用於緩存 IP 和 MAC 地址的映射關係
這三張表是怎麼來的
MAC 地址表是經過以太網內各節點之間不斷經過交換機通訊,不斷完善起來的。
路由表是各類路由算法 + 人工配置逐步完善起來的。
arp 緩存表是不斷經過 arp 協議的請求逐步完善起來的。
知道了以上這些,目前網絡上兩個節點是如何發送數據包的這個過程,就徹底能夠解釋通了!
那接下來咱們就放上參考的 最後一個網絡拓撲圖吧,請作好 戰鬥 準備!
這時路由器 1 鏈接了路由器 2,因此其路由表有了下一條地址這一個概念,因此它的路由表就變成了這個樣子。若是匹配到了有下一跳地址的一項,則須要再次匹配,找到其端口,並找到下一跳 IP 的 MAC 地址。
也就是說找來找去,最終必須能映射到一個端口號,而後從這個端口號把數據包發出去。
目的地址 | 下一跳 | 端口 |
---|---|---|
192.168.0.0/24 | 0 | |
192.168.0.254/32 | 0 | |
192.168.1.0/24 | 1 | |
192.168.1.254/32 | 1 | |
192.168.2.0/24 | 192.168.100.5 | |
192.168.100.0/24 | 2 | |
192.168.100.4/32 | 2 |
思考一分鐘...
詳細過程動畫描述:
1. 首先 A(192.168.0.1)經過子網掩碼(255.255.255.0)計算出本身與 F(192.168.2.2)並不在同一個子網內,因而決定發送給默認網關(192.168.0.254)
2. A 經過 ARP 找到 默認網關 192.168.0.254 的 MAC 地址。
3. A 將源 MAC 地址(AAAA)與網關 MAC 地址(ABAB)封裝在數據鏈路層頭部,又將源 IP 地址(192.168.0.1)和目的 IP 地址(192.168.2.2)(注意這裏千萬不要覺得填寫的是默認網關的 IP 地址,從始至終這個數據包的兩個 IP 地址都是不變的,只有 MAC 地址在不斷變化)封裝在網絡層頭部,而後發包
4. 交換機 1 收到數據包後,發現目標 MAC 地址是 ABAB,轉發給路由器1
5. 數據包來到了路由器 1,發現其目標 IP 地址是 192.168.2.2,查看其路由表,發現了下一跳的地址是 192.168.100.5*
6. 因此此時路由器 1 須要作兩件事,第一件是再次匹配路由表,發現匹配到了端口爲 2,因而將其封裝到數據鏈路層,最後把包從 2 號口發出去。
7. 此時路由器 2 收到了數據包,看到其目的地址是 192.168.2.2,查詢其路由表,匹配到端口號爲 1,準備從 1 號口把數據包送出去。
8. 但此時路由器 2 須要知道 192.168.2.2 的 MAC 地址了,因而查看其 arp 緩存,找到其 MAC 地址爲 FFFF,將其封裝在數據鏈路層頭部,並從 1 號端口把包發出去。
9. 交換機 3 收到了數據包,發現目的 MAC 地址爲 FFFF,查詢其 MAC 地址表,發現應該從其 6 號端口出去,因而從 6 號端口把數據包發出去。
10.F 最終收到了數據包!**而且發現目的 MAC 地址就是本身,因而收下了這個包
利用TCP/IP進行網絡通訊時,數據包會按照分層順序與對方進行通訊。發送端從應用層往下走,接收端從鏈路層往上走。從客戶端到服務器的數據,每一幀數據的傳輸的順序都爲:應用層->運輸層->網絡層->鏈路層->鏈路層->網絡層->運輸層->應用層。
以一個HTTP請求的傳輸爲例,請求從HTTP客戶端(如瀏覽器)和HTTP服務端應用的傳輸過程,大體以下圖所示:
圖:HTTP請求報文的分層傳輸過程
接下來,爲你們介紹一下數據封裝和分用。
數據經過互聯網傳輸的時候不多是光禿禿的不加標識,若是這樣數據就會亂。因此數據在發送的時候,須要加上特定標識,加上特定標識的過程叫作數據的封裝,在數據使用的時候再去掉特定標識,去掉特定標識的過程就叫作分用。TCP/IP協議的數據封裝和分用過程,大體以下圖所示:
圖:TCP/IP協議的數據封裝和分用過程
在數據封裝時,數據通過每一個層都會打上該層特定標識,添加上頭部。
在傳輸層封裝時,添加的報文首部時要存入一個應用程序的標識符,不管TCP和UDP都用一個16位的端口號來表示不一樣的應用程序,而且都會將源端口和目的端口存入報文首部中。
在網絡層封裝時,IP首部會標識處理數據的協議類型,或者說標識出網絡層數據幀所攜帶的上層數據類型,如TCP、UDP、ICMP、IP、IGMP等等。
具體來講,會在IP首部中存入一個長度爲8位的數值,稱做協議域:
1表示爲ICMP協議、2表示爲IGMP協議、6表示爲TCP協議、17表示爲UDP協議、等等。IP首部還會標識發送方地址(源IP)和接收方地址(目標IP)。
在鏈路層封裝時,網絡接口分別要發送和接收IP、ARP和RARP等多種不一樣協議的報文,所以也必須在以太網的幀首部中加入某種形式的標識,以指明所處理的協議類型,爲此,以太網的報文幀的首部也有一個16位的類型域,標識出以太網數據幀所攜帶的上層數據類型,如IPv四、ARP、IPV六、PPPoE等等。
數據封裝和分用的過程大體爲:發送端每經過一層會增長該層的首部,接收端每經過一層則刪除該層的首部。
整體來講,TCP/IP分層管理、數據封裝和分用的好處:分層以後若需改變相關設計,只需替換變更的層。各層之間的接口部分規劃好以後,每一個層次內部的設計就能夠自由改動。層次化以後,設計也變得相對簡單:各個層只需考慮分派給本身的傳輸任務。
TCP/IP與OSI的區別主要有哪些呢?除了TCP/IP與OSI在分層模塊上稍有區別,更重要的區別爲:OSI參考模型注重「通訊協議必要的功能是什麼」,而TCP/IP則更強調「在計算機上實現協議應該開發哪一種程序」。
實際上,在傳輸過程當中,數據報文會在不一樣的物理網絡之間傳遞,仍是以一個HTTP請求的傳輸爲例,請求在不一樣物理網絡之間的傳輸過程,大體以下圖所示:
圖:HTTP請求在不一樣物理網絡之間的傳輸過程
數據包在不一樣物理網絡之間的傳輸過程當中,網絡層會經過路由器去對不一樣的網絡之間的數據包進行存儲、分組轉發處理。構造互連網最簡單的方法是把兩個或多個網絡經過路由器進行鏈接。路由器能夠簡單理解爲一種特殊的用於網絡互連的硬件盒,其做用是爲不一樣類型的物理網絡提供鏈接:以太網、令牌環網、點對點的連接和FDDI(光纖分佈式數據接口)等等。
物理網絡之間經過路由器進行互連,隨着增長不一樣類型的物理網絡,可能會有不少個路由器,可是對於應用層來講仍然是同樣的,TCP協議棧爲你們屏蔽了物理層的複雜性。總之,物理細節和差別性的隱藏,使得互聯網TCP/IP傳輸的功能變得很是強大。
接下來,開始爲你們介紹與傳輸性能有密切關係的內容:TCP傳輸層的三次握手創建鏈接,四次揮手釋放鏈接。不過在此以前,還得先介紹一下TCP報文協議。
在TCP/IP協議棧中,IP協議層只關心如何使數據可以跨越本地網絡邊界的問題,而不關心數據如何傳輸。總體TCP/IP協議棧,共同配合一塊兒解決數據如何經過許許多多個點對點通路,順利傳輸到達目的地。一個點對點通路被稱爲一「跳」(hop),經過TCP/IP協議棧,網絡成員可以在許多「跳」的基礎上創建相互的數據通路。
傳輸層TCP協議提供了一種面向鏈接的、可靠的字節流服務,其數據幀格式,大體以下圖所示:
圖:傳輸層TCP協議的數據幀格式
一個傳輸層TCP協議的數據幀,大體包含如下字段:
源端口號表示報文的發送端口,佔16位。源端口和源IP地址組合起來,能夠標識報文的發送地址。
目的端口號表示報文的接收端口,佔16位。目的端口和目的IP地址相結合,能夠標識報文的接收地址。
TCP協議是基於IP協議的基礎上傳輸的,TCP報文中的源端口號+源IP,與TCP報文中的目的端口號+目的IP一塊兒,組合起來惟一性的肯定一條TCP鏈接。
TCP傳輸過程當中,在發送端出的字節流中,傳輸報文中的數據部分的每個字節都有它的編號。序號(Sequence
Number)佔32位,發起方發送數據時,都須要標記序號。
序號(Sequence Number)的語義與SYN控制標誌(Control
Bits)的值有關。根據控制標誌(Control Bits)中的SYN是否爲1,序號(Sequence
Number)表達不一樣的含義:
(1)當SYN = 1時,當前爲鏈接創建階段,此時的序號爲初始序號ISN((Initial Sequence
Number),經過算法來隨機生成序號;
(2)當SYN = 0時在數據傳輸正式開始時,第一個報文的序號爲 ISN +
1,後面的報文的序號,爲前一個報文的SN值+TCP報文的淨荷字節數(不包含TCP頭)。好比,若是發送端發送的一個TCP幀的淨荷爲12byte,序號爲5,則發送端接着發送的下一個數據包的時候,序號的值應該設置爲5+12=17。
在數據傳輸過程當中,TCP協議經過序號(Sequence
Number)對上層提供有序的數據流。發送端能夠用序號來跟蹤發送的數據量;接收端能夠用序號識別出重複接收到的TCP包,從而丟棄重複包;對於亂序的數據包,接收端也能夠依靠序號對其進行排序。
確認序號(Acknowledgment
Number)標識了報文接收端指望接收的字節序列。若是設置了ACK控制位,確認序號的值表示一個準備接收的包的序列碼,注意,它所指向的是準備接收的包,也就是下一個指望接收的包的序列碼。
舉個例子,假設發送端(如Client)發送3個淨荷爲1000byte、起始SN序號爲1的數據包給Server服務端,Server每收到一個包以後,須要回覆一個ACK響應確認數據包給Client。ACK響應數據包的ACK
Number值,爲每一個Client包的爲SN+包淨荷,既表示Server已經確認收到的字節數,還表示指望接收到的下一個Client發送包的SN序號,具體的ACK值以下圖左邊的正常傳輸部分所示。
圖:傳輸過程的確認序號(Acknowledgment Number)值示例圖
在上圖的左邊部分,Server第1個ACK包的ACK
Number值爲1001,是經過Client第1個包的SN+包淨荷=1+1000計算獲得,表示指望第2個Client包的SN序號爲1001;Server第2個ACK包的ACK
Number值爲2001,爲Client第2個包的SN+包淨荷=2001,表示指望第3個Server包的SN爲2001,以此類推。
若是發生錯誤,假設Server在處理Client的第二個發送包異常,Server仍然回覆一個ACK
Number值爲1001的確認包,則Client的第二個數據包須要重複發送,具體的ACK值如上圖右邊的正常傳輸部分所示。
只有控制標誌的ACK標誌爲1時,數據幀中的確認序號ACK
Number纔有效。TCP協議規定,鏈接創建後,全部發送的報文的ACK必須爲1,也就是創建鏈接後,全部報文的確認序號有效。若是是SYN類型的報文,其ACK標誌爲0,故沒有確認序號。
該字段佔用4位,用來表示TCP報文首部的長度,單位是4bit位。其值所表示的並非字節數,而是頭部的所含有的32bit的數目(或者倍數),或者4個字節的倍數,因此TCP頭部最多能夠有60字節(4*15=60)。沒有任何選項字段的TCP頭部長度爲20字節,因此其頭部長度爲5,能夠經過20/4=5計算獲得。
頭部長度後面預留的字段長度爲6位,做爲保留字段,暫時沒有什麼用處。
控制標誌(Control
Bits)共6個bit位,具體的標誌位爲:URG、ACK、PSH、RST、SYN、FIN。6個標誌位的說明,以下表所示。
表:TCP報文控制標誌(Control Bits)說明
標誌位 | 說明 |
---|---|
URG | 佔1位,表示緊急指針字段有效。URG位指示報文段裏的上層實體(數據)標記爲「緊急」數據。當URG=1時,其後的緊急指針指示緊急數據在當前數據段中的位置(相對於當前序列號的字節偏移量),TCP接收方必須通知上層實體。 |
ACK | 佔1位,置位ACK=1表示確認號字段有效;TCP協議規定,接創建後全部發送的報文的ACK必須爲1;當ACK=0時,表示該數據段不包含確認信息。當ACK=1時,表示該報文段包括一個對已被成功接收報文段的確認序號Acknowledgment Number,該序號同時也是下一個報文的預期序號。 |
PSH | 佔1位,表示當前報文須要請求推(push)操做;當PSH=1時,接收方在收到數據後當即將數據交給上層,而不是直到整個緩衝區滿。 |
RST | 佔1位,置位RST=1表示復位TCP鏈接;用於重置一個已經混亂的鏈接,也可用於拒絕一個無效的數據段或者拒絕一個鏈接請求。若是數據段被設置了RST位,說明報文發送方有問題發生。 |
SYN | 佔1位,在鏈接創建時用來同步序號。當SYN=1而ACK=0時,代表這是一個鏈接請求報文。對方若贊成創建鏈接,則應在響應報文中使SYN=1和ACK=1。 綜合一下,SYN置1就表示這是一個鏈接請求或鏈接接受報文。 |
FIN | 佔1位,用於在釋放TCP鏈接時,標識發送方比特流結束,用來釋放一個鏈接。當 FIN = 1時,代表此報文的發送方的數據已經發送完畢,並要求釋放鏈接。 |
在鏈接創建的三次握手過程當中,若只是單個SYN置位,表示的只是創建鏈接請求。若是SYN和ACK同時置位爲1,表示的創建鏈接以後的響應。
長度爲16位,共2個字節。此字段用來進行流量控制。流量控制的單位爲字節數,這個值是本端指望一次接收的字節數。
長度爲16位,共2個字節。對整個TCP報文段,即TCP頭部和TCP數據進行校驗和計算,接收端用於對收到的數據包進行驗證。
長度爲16位,2個字節。它是一個偏移量,和SN序號值相加表示緊急數據最後一個字節的序號。
以上十項內容是TCP報文首部必須的字段,也稱固有字段,長度爲20個字節。接下來是TCP報文的可選項和填充部分。
可選項和填充部分的長度爲4n字節(n是整數),該部分是根據須要而增長的選項。若是不足4n字節,要加填充位,使得選項長度爲32位(4字節)的整數倍,具體的作法是在這個字段中加入額外的零,以確保TCP頭是32位(4字節)的整數倍。
最多見的選項字段是MSS(Maximum Segment
Size最長報文大小),每一個鏈接方一般都在通訊的第一個報文段(SYN標誌爲1的那個段)中指明這個選項字段,表示當前鏈接方所能接受的最大報文段的長度。
因爲可選項和填充部分不是必須的,因此TCP報文首部最小長度爲20個字節。
至此,TCP報文首部的字段,就所有介紹完了。TCP報文首部的後面,接着的是數據部分,不過數據部分是可選的。在一個鏈接創建和一個鏈接終止時,雙方交換的報文段僅有TCP首部。若是一方沒有數據要發送,也使用沒有任何數據的首部來確認收到的數據,好比在處理超時的過程當中,也會發送不帶任何數據的報文段。
整體來講,TCP協議的可靠性,主要經過如下幾點來保障:
(1)應用數據分割成TCP認爲最適合發送的數據塊。這部分是經過MSS(最大數據包長度)選項來控制的,一般這種機制也被稱爲一種協商機制,MSS規定了TCP傳往另外一端的最大數據塊的長度。值得注意的是,MSS只能出如今SYN報文段中,若一方不接收來自另外一方的MSS值,則MSS就定爲536字節。通常來說,MSS值仍是越大越好,這樣能夠提升網絡的利用率。
(2)重傳機制。設置定時器,等待確認包,若是定時器超時尚未收到確認包,則報文重傳。
(3)對首部和數據進行校驗。
(4)接收端對收到的數據進行排序,而後交給應用層。
(5)接收端丟棄重複的數據。
(6)TCP還提供流量控制,主要是經過滑動窗口來實現流量控制。
至此TCP協議的數據幀格式介紹完了。接下來開始爲你們重點介紹:TCP傳輸層的三次握手創建鏈接,四次揮手釋放鏈接。
TCP鏈接的創建時,雙方須要通過三次握手,而斷開鏈接時,雙方須要通過四次分手,那麼,其三次握手和四次分手分別作了什麼呢?又是如何進行的呢?
一般狀況下,創建鏈接的雙方,由一端打開一個監聽套接字(ServerSocket)來監聽來自請求方的TCP(Socket)鏈接,當服務器端監聽開始時,必須作好準備接受外來的鏈接,在Java中該操做經過建立一個ServerSocket服務監聽套接字實例來完成,此操做會調用底層操做系統(如Linux)的C代碼中三個函數socket()、bind()、listen()
來完成。開始監聽以後,服務器端就作好接受外來鏈接的準備,若是監聽到創建新鏈接的請求,會開啓一個傳輸套接字,稱之爲被動打開(Passive
Open)。
一段簡單的服務端監聽新鏈接請求,而且被動打開(Passive
Open)傳輸套接字的Java示例代碼,具體以下:
public class SocketServer { public static void main(String[] args) { try { // 建立服務端socket ServerSocket serverSocket = new ServerSocket(8080); //循環監聽等待客戶端的鏈接 while(true){ //監聽到客戶端鏈接,傳輸套接字被動開啓 Socket socket = serverSocket.accept(); //開啓線程進行鏈接的IO處理 ServerThread thread = new ServerThread(socket); thread.start(); ...... } } catch (Exception e) { // 處理異常 e.printStackTrace(); } } }
客戶端在發起鏈接創建時,Java代碼經過建立Socket實例,調用底層的connect(…)方法,主動打開(Active
Open)Socket鏈接。套接字監聽方在收到請求以後,監聽方和發起方(客戶端)之間就會創建一條的鏈接通道,該通道由雙方IP和雙方端口所惟一肯定。
一段簡單的客戶端鏈接主動打開(Active Open)的Java示例代碼,具體以下:
public class SocketClient { public static void main(String[] args) throws InterruptedException { try { // 和服務器建立鏈接 Socket socket = new Socket("localhost",8080); // 寫入給監聽方的輸出流 OutputStream os = socket.getOutputStream(); ….. // 讀取監聽方的輸入流 InputStream is = socket.getInputStream(); ….. } catch (Exception e) { e.printStackTrace(); } } }
TCP鏈接的創建時,雙方須要通過三次握手,具體過程以下:
(1)第一次握手:Client進入SYN_SENT狀態,發送一個SYN幀來主動打開傳輸通道,該幀的SYN標誌位被設置爲1,同時會帶上Client分配好的SN序列號,該SN是根據時間產生的一個隨機值,一般狀況下每間隔4ms會加1。除此以外,SYN幀還會帶一個MSS(最大報文段長度)可選項的值,表示客戶端發送出去的最大數據塊的長度。
(2)第二次握手:Server端在收到SYN幀以後,會進入SYN_RCVD狀態,同時返回SYN+ACK幀給Client,主要目的在於通知Client,Server端已經收到SYN消息,如今須要進行確認。Server端發出的SYN+ACK幀的ACK標誌位被設置爲1,其確認序號AN(Acknowledgment
Number)值被設置爲Client的SN+1;SYN+ACK幀的SYN標誌位被設置爲1,SN值爲Server端生成的SN序號;SYN+ACK幀的MSS(最大報文段長度)表示的是Server端的最大數據塊長度。
(3)第三次握手:Client在收到Server的第二次握手SYN+ACK確認幀以後,首先將本身的狀態會從SYN_SENT變成ESTABLISHED,表示本身方向的鏈接通道已經創建成功,Client能夠發送數據給Server端了。而後,Client發ACK幀給Server端,該ACK幀的ACK標誌位被設置爲1,其確認序號AN(Acknowledgment
Number)值被設置爲Server端的SN序列號+1。還有一種狀況,Client可能會將ACK幀和第一幀要發送的數據,合併到一塊兒發送給Server端。
(4)Server端在收到Client的ACK幀以後,會從SYN_RCVD狀態會進入ESTABLISHED狀態,至此,Server方向的通道鏈接創建成功,Server能夠發送數據給Client,TCP的全雙工鏈接創建完成。
三次握手的交互過程,具體以下圖所示:
圖:TCP創建的鏈接時三次握手示意圖
Client和Server完成了三次握手後,雙方就進入了數據傳輸的階段。數據傳輸完成後,鏈接將斷開,鏈接斷開的過程須要經歷四次揮手。
業務數據通訊完成以後,TCP鏈接開始斷開(或者拆接)的過程,在這個過程當中鏈接的每一個端的都能獨立地、主動的發起,斷開的過程TCP協議使用了四路揮手操做。
四次揮手具體過程,具體以下:
(1)第一次揮手:主動斷開方(能夠是客戶端,也能夠是服務器端),向對方發送一個FIN結束請求報文,此報文的FIN位被設置爲1,而且正確設置Sequence
Number(序列號)和Acknowledgment
Number(確認號)。發送完成後,主動斷開方進入FIN_WAIT_1狀態,這表示主動斷開方沒有業務數據要發送給對方,準備關閉SOCKET鏈接了。
(2)第二次揮手:正常狀況下,在收到了主動斷開方發送的FIN斷開請求報文後,被動斷開方會發送一個ACK響應報文,報文的Acknowledgment
Number(確認號)值爲斷開請求報文的Sequence Number
(序列號)加1,該ACK確認報文的含義是:「我贊成你的鏈接斷開請求」。以後,被動斷開方就進入了CLOSE-WAIT(關閉等待)狀態,TCP協議服務會通知高層的應用進程,對方向本地方向的鏈接已經關閉,對方已經沒有數據要發送了,若本地還要發送數據給對方,對方依然會接受。被動斷開方的CLOSE-WAIT(關閉等待)還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
主動斷開方在收到了ACK報文後,由FIN_WAIT_1轉換成FIN_WAIT_2狀態。
(3)第三次揮手:在發送完成ACK報文後,被動斷開方還能夠繼續完成業務數據的發送,待剩餘數據發送完成後,或者CLOSE-WAIT(關閉等待)截止後,被動斷開方會向主動斷開方發送一個FIN+ACK結束響應報文,表示被動斷開方的數據都發送完了,而後,被動斷開方進入LAST_ACK狀態。
(4)第四次揮手:主動斷開方收在到FIN+ACK斷開響應報文後,還須要進行最後的確認,向被動斷開方發送一個ACK確認報文,而後,本身就進入TIME_WAIT狀態,等待超時後最終關閉鏈接。處於TIME_WAIT狀態的主動斷開方,在等待完成2MSL的時間後,若是期間沒有收到其餘報文,則證實對方已正常關閉,主動斷開方的鏈接最終關閉。
被動斷開方在收到主動斷開方的最後的ACK報文之後,最終關閉了鏈接,本身啥也無論了。
四次揮手的所有交互過程,具體以下圖所示:
圖:TCP創建的鏈接時四次揮手的示意圖
處於TIME_WAIT狀態的主動斷開方,在等待完成2MSL的時間後,才真正關閉鏈接通道,其等待的時間爲何是2MSL呢?
2MSL翻譯過來就是兩倍的MSL。MSL全稱爲Maximum Segment
Lifetime,指的是一個TCP報文片斷在網絡中最大的存活時間,具體來講,2MSL對應於一次消息的來回(一個發送和一個回覆)所需的最大時間。若是直到2MSL,主動斷開方都沒有再一次收到對方的報文(如FIN報文),則能夠推斷ACK已經被對方成功接收,此時,主動斷開方將最終結束本身的TCP鏈接。因此,TCP的TIME_WAIT狀態也稱爲2MSL等待狀態。
有關MSL的具體的時間長度,在RFC1122協議中推薦爲2分鐘。在SICS(瑞典計算機科學院)開發的一個小型開源的TCP/IP協議棧——LwIP開源協議棧中MSL默認爲1分鐘。在源自Berkeley的TCP協議棧實現中MSL默認長度爲30秒。整體來講,TIME_WAIT(2MSL)等待狀態的時間長度,通常維持在1-4分鐘之間。
經過三次握手創建鏈接和四次揮手拆除鏈接,一次TCP的鏈接創建及拆除,至少進行7次通訊,可見其成本是很高的。
有關TCP的鏈接創建的三次握手及拆除過程的四次揮手的面試問題,是技術面試過程當中的出現頻率很高的重點和難點問題,常見問題大體以下:
關閉鏈接時,被動斷開方在收到對方的FIN結束請求報文時,極可能業務數據沒有發送完成,並不能當即關閉鏈接,被動方只能先回復一個ACK響應報文,告訴主動斷開方:「你發的FIN報文我收到了,只有等到我全部的業務報文都發送完了,我才能真正的結束,在結束以前,我會發你FIN+ACK報文的,你先等着」。因此,被動斷開方的確認報文,須要拆開成爲兩步,故整體就須要四步揮手。
而在創建鏈接場景中,Server端的應答能夠稍微簡單一些。當Server端收到Client端的SYN鏈接請求報文後,其中ACK報文表示對請求報文的應答,SYN報文用來表示服務端的鏈接也已經同步開啓了,而ACK報文和SYN報文之間,不會有其餘報文須要發送,故而能夠合二爲一,能夠直接發送一個SYN+ACK報文。因此,在創建鏈接時,只須要三次握手便可。
三次握手完成兩個重要的功能:一是雙方都作好發送數據的準備工做,並且雙方都知道對方已準備好;二是雙方完成初始SN序列號的協商,雙方的SN序列號在握手過程當中被髮送和確認。
若是把三次握手改爲兩次握手,可能發生死鎖。兩次握手的話,缺失了Client的二次確認ACK幀,假想的TCP創建的鏈接時二次揮手,能夠以下圖所示:
圖:假想的TCP創建的鏈接時二次握手的示意圖
在假想的TCP創建的鏈接時二次握手過程當中,Client發送Server發送一個SYN請求幀,Server收到後發送了確認應答SYN+ACK幀。按照兩次握手的協定,Server認爲鏈接已經成功地創建了,能夠開始發送數據幀。這個過程當中,若是確認應答SYN+ACK幀在傳輸中被丟失,Client沒有收到,Client將不知道Server是否已準備好,也不知道Server的SN序列號,Client認爲鏈接還未創建成功,將忽略Server發來的任何數據分組,會一直等待Server的SYN+ACK確認應答幀。而Server在發出的數據幀後,一直沒有收到對應的ACK確認後就會產生超時,重複發送一樣的數據幀。這樣就造成了死鎖。
緣由之一:主動斷開方等待2MSL的時間,是爲了確保兩端都能最終關閉。假設網絡是不可靠的,被動斷開方發送FIN+ACK報文後,其主動方的ACK響應報文有可能丟失,這時候的被動斷開方處於LAST-ACK狀態的,因爲收不到ACK確認被動方一直不能正常的進入CLOSED狀態。在這種場景下,被動斷開方會超時重傳FIN+ACK斷開響應報文,若是主動斷開方在2MSL時間內,收到這個重傳的FIN+ACK報文,會重傳一次ACK報文,後再一次從新啓動2MSL計時等待,這樣,就能確保被動斷開方能收到ACK報文,從而能確保被動方順利進入到CLOSED狀態。只有這樣,雙方都可以確保關閉。反過來講,若是主動斷開方在發送完ACK響應報文後,不是進入TIME_WAIT狀態去等待2MSL時間,而是當即釋放鏈接,則將沒法收到被動方重傳的FIN+ACK報文,因此不會再發送一次ACK確認報文,此時處於LAST-ACK狀態的被動斷開方,沒法正常進入到CLOSED狀態。
緣由之二:防止「舊鏈接的已失效的數據報文」出如今新鏈接中。主動斷開方在發送完最後一個ACK報文後,再通過2MSL,才能最終關閉和釋放端口,這就意味着,相同端口的新TCP新鏈接,須要在2MSL的時間以後,纔可以正常的創建。2MSL這段時間內,舊鏈接所產生的全部數據報文,都已經從網絡中消失了,從而,確保了下一個新的鏈接中不會出現這種舊鏈接請求報文。
TCP還設有一個保活計時器,Client端若是出現故障,Server端不能一直等下去,這樣會浪費系統資源。每收到一次Client客戶端的數據幀後,Server端都的保活計時器會復位。計時器的超時時間一般是設置爲2小時,若2小時尚未收到Client端的任何數據幀,Server端就會發送一個探測報文段,之後每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,Server端就認爲Client端出了故障,接着就關閉鏈接。若是以爲保活計時器的兩個多小時的間隔太長,能夠自行調整TCP鏈接的保活參數。