兩萬字長文50+張趣圖帶你領悟網絡編程的內功心法

前言

我大學是學網絡工程專業,也就是那種拉網線,面向網線編程的。依稀記得學習計算機網絡這門課程的時候搭建的IT宅 itzhai.com我的網站。html

算一下,學這門課程也已經快十年了。node

某一天,偶然又看到了這本書:git

image-20200726213509847

翻了下,發現裏面的內容居然仍是絕不過期,真的是越底層的知識越有價值呀。我擦了擦書面的灰塵,決定要爲它寫點什麼,因而又從書架上找了相關的書籍:github

image-20200726211636509

來回翻閱和梳理總結,逐漸輸出了這篇文章,獻給對網絡不太熟悉,又想快速從入門到熟練的朋友們。web

相信你們拿到Socket API,就能夠很快寫好代碼,收發消息,傳送文件什麼的,但是底層究竟發生了什麼?TCP、UDP、HTTP是什麼關係、爲啥要有WebSocket編程。咱們從TCP/IP協議棧以及一根網線提及,逐步揭開面向網線編程內功心法的面紗。面試

最後,在這裏解答一個問題:有人問我爲何要寫公衆號技術文章呢?工做越久,發現身邊比本身年紀小的人越多,我也時常在想,那些同齡人或者比我大的人都去哪裏了,也許有些人忙於家庭生活不亦樂乎,有些人由於公司上市拿到可觀的收入轉行了,也許有人在大公司作起了管理工做,開始走管理路線,帶領團隊創造新的產品。我寫公衆號的緣由之一,也就是想告訴你們,我一直在作技術,一個堅持寫代碼的大齡技術人,而且但願可以結實更多志同道合的技術人。沒錯,在說大家呢,不要求三連,這篇文章對你感興趣就點個在看唄。Thanks♪(・ω・)ノ算法

本文的各類電腦、服務器、路由器小圖標都是我一筆一筆畫的用心只作了儘可能美觀有趣好理解的配圖,旨在但願可以有助於你們理解文章內容,真心但願產品經理也能夠看懂。sql

一、網絡分層

1.一、OSI七層模型

爲了制定一個統一的計算機網絡體系,國際標準化組織ISO提出了一個試圖使各類計算機能夠在世界範圍內互聯成網的標準框架:OSI/RM(Open System Interconnection Reference Model 開放系統互連基本參考模型),該模型以下:shell

image-20200708085822940

媒介層:第一到第三層稱爲媒體層,它們主要與硬件相關,例如路由,交換和電纜規格;數據庫

主機層:第四到第七層稱爲主機層,它們是實現網絡服務相關的軟件。

大體介紹一下各層(注意:看看就好,這不是重點,重點是後面的TCP/IP協議):

  • 物理層:物理層負責在設備和物理傳輸介質直接傳輸和接收非結構化原始數據。這一層中,把數字位轉換爲電,無線電或光信號。能夠發現這一層每每跟各類材質啊信號呀什麼的打交道,因此稱爲物理層;
  • 數據鏈路層:在經過物理層鏈接的兩個節點之間進行傳輸數據幀,檢測而且糾正物理層中可能發生的錯誤。它定義了在兩個物理鏈接的設備之間創建和終止鏈接的協議,還定義了他們之間的流控制協議;
  • 網絡層:構建和管理多節點網絡,包括尋址、路由、流量控制。網絡層是一種能夠鏈接許多節點的介質,每一個節點都在其上有一個地址,經過目標地址就能夠在節點之間傳輸數據到目標地址。網絡層消息傳輸不必定要保證可靠,網絡層系能夠能夠提供可靠的消息傳遞,但沒必要這樣作;
  • 傳輸層:傳輸層提供了將可變長度數據序列從源傳輸到目標主機的功能和過程方法,同時又保持了服務功能的質量。一些協議是面向狀態和麪向鏈接的,這意味着傳輸層能夠進行分段傳輸、支持失敗重傳;
  • 會話層:控制計算機之間的鏈接,負責創建、管理和終止本地和遠程引用程序之間的鏈接,提供全雙工、半雙工或者單工操做;
  • 表示層:網絡服務和應用程序之間的數據轉換,如字符編碼、數據壓縮、加密解密;
  • 應用層:最接近最終用戶的OSI層,該層直接與實現通訊組件的軟件應用程序進行交互。

能夠發現,這個模型還真有點複雜。但很惋惜,這個模型彷佛不怎麼流行,緣由以下:

  • OSI專家缺少實際經驗,缺乏商業驅動;
  • OSI協議實現過於複雜,運行效率低;
  • 標準制定週期長,市場已被其餘標準佔據;
  • 層次劃分不太合理,部分功能在多個分層中出現。

在1980年代末和1990年代初的一段時間內,工程師、組織和國家對哪一種標準(OSI模型或Internet協議套件)將更能塑造最佳和最強大的計算機網絡存在爭端,致使兩極分化。儘管OSI在1980年代後期開發了其網絡標準,後來更多的供應商網絡上更多采用的倒是TCP / IP標準,最終TCP/IP成爲了實時的國際標準。

1.二、TCP/IP四層模型

下面咱們把TCP/IP模型和OSI模型放一塊兒對比下:

image-20200708234353206

能夠發現,TCP/IP體系少了表示層和會話層,數據鏈路層和物理層用鏈路層取代。

  • 應用層:最高層,應用層的任務是經過應用進程間交互來實現特定網絡應用。主要負責把應用程序中的用戶數據傳達給另外一臺主機或同一主機上的其餘應用程序。這是全部應用程序協議的運行層,如SMTP、FTP、SSH、HTTP等;
  • 傳輸層:負責向兩個主機中的進程之間的通訊提供通用的數據傳輸服務。UDP是基本的傳輸層協議,提供了不可靠的無鏈接數據報傳輸服務;
  • 網絡層:負責爲分組交換網上的不一樣主機提供通訊服務。該層定義了尋址和路由功能,主要協議是IP協議(Internet Protocol),它定義了IP地址,它在路由中的功能是將數據報傳輸到充當IP路由器的下一個主機,該主機更接近最終數據目的地;
  • 鏈路層:也稱爲數據鏈路層或者網絡接口層,一般包括操做系統中設備驅動程序和計算機對應的網絡接口卡。它們負責處理與傳輸媒介的物理接口細節。

然後面咱們除了講到各類協議以外,還會順便說起一些底層硬件,爲了更好的進行闡述,咱們將把鏈路層再分爲物理層和數據鏈路層,採用如下這種五層模型:

image-20200711112221679

根據運行模式分爲如下兩種:

  • 運行於用戶進程:應用層,關注應用程序的細節,不關注底層網絡通訊細節;
  • 運行於內核:傳輸層、網絡層、鏈路層,在內核中執行,主要處理全部的通訊細節。

介紹了這麼多概念,是否是比較難懂呢,不要緊,咱們列一下每一層主要的協議,接下來咱們會詳細的講解各類協議的原理。

1.三、分層模型如何工做

這裏咱們經過一個FTP客戶端的通訊流程,來講明下兩臺主機是如何工做在TCP/IP分層模型上的:

image-20200711135012000

如上圖,A主機的FTP客戶端要與B主機的FTP服務器進行交互。

咱們對設備作一下劃分:

  • 端系統(End system):A主機和B主機
  • 中間系統(Intermediate system):路由器

其中用到的協議也作一下劃分:

  • 端對端協議(End-to-end):包括應用層和傳輸層,端系統直接直接進行交互;
  • 逐跳(Hop-by-hop)協議:網絡層,須要通過端系統中全部的中間系統。

對於應用層來講,他們好像是直接與端系統進行交互的,應用層根本不知道底層通訊用了多少個路由器,是在以太網上仍是在令牌環網上的。

什麼是三層設備,二層設備?

如上圖,路由器工做在網絡層,屬於第三層,因此常常有人稱他爲三層設備;然後面咱們會講到交換機,他是工做在第二層-數據鏈路層,因此也成爲二層設備。

1.3.一、爲何要分層?

正如上面的例子,分層以後是的頂層屏蔽了底層的物理和通訊細節。底層的通訊原理是在較低的協議層中實現的,所以每一個進程都將隱藏大多數通訊細節。以此類推,在傳輸層,通訊表現爲主機到主機,而無需瞭解應用程序數據結構和鏈接的路由器。而在互聯網絡層,則在每一個路由器上遍歷各個網絡邊界。

就像咱們搞軟件開發劃分層次同樣,分層以後,提升了軟件的複用度,封裝每層細節,使用者只須要關注使用的API就能夠了,不用關注實現細節。你不要告訴我你的一個功能涉及的一萬行代碼是寫在一個函數裏面的,那太可怕了。

想要了解底層細節的人,就只能拆開TCP/IP協議潘多拉之盒,逐個協議去了解了,這也是本文後邊會繼續探討的內容。高度的封裝,使得頂層開發人員能更快速的經過API開發應用程序。做爲一個API工程師,你知道怎麼調用API發送HTTP請求就夠了,可是做爲一個有追求的工程師,你瞭解了這些細節以後,就可以勝任程序調優以及更加底層的開發工做了。這也是爲何我堅持寫IT宅 itzhai.com博客的緣由:我想探索技術的本質,而不是生活在API構造的童話世界裏面,這樣即便童話世界謊話被拆穿的那天,也不至於失掉技術的信仰,由於我仍然有能力構建一個新的童話世界。

image-20200711120503245

接下來咱們看看數據包從在傳輸過程當中是如何封裝的。

1.3.二、數據包的封裝和分用

爲了演示,咱們須要構建一個局域網(LAN)。

假設咱們如今直接經過兩個網線把兩臺電腦連起來進行通訊,須要作哪些工做呢。多虧我大學學的是網絡工程,也是拉過網線的,因此多少還知道一點:

  • 準備一根網線,兩個水晶頭;
  • 水晶頭要作交叉線,採用1-3,2-6交叉接法,保證兩個水晶頭之間可以正常收發信號;
  • 把兩個水晶頭分別插在兩臺主機的電腦上;
  • 給兩臺電腦配置IP、子網掩碼和網關,必需要配置到同一個網絡中。

這樣咱們就構建好了一個最簡單的局域網了。

image-20200726223529480

1.3.2.一、封裝

所謂封裝,就是每一層都會根據用到的協議,把數據封裝成最終的一個數據單元(不一樣分層有不一樣的叫法,參考上圖最左邊每層的描述),每一層拿到的上一層的內容,把上一層封裝好的內容做爲當前層的數據,而後加上本身的協議頭或者尾,接着執行該層協議的相關處理邏輯。

有沒有發現,這有點像裝飾者模式,每一層拿到上一層的內容以後都添加額外的處理邏輯,可是不改變上層傳過來的內容。

image-20200726223316970

如上圖,左邊部分爲封裝的過程:

  • A主機請求B主機的HTTP服務,應用層使用HTTP協議對請求內容進行封裝,加上HTTP請求頭,而後傳給下一層;
  • 傳輸層拿到HTTP數據,使用TCP協議進行處理,加上本身的TCP頭,封裝成數據段,經過TCP協議傳輸給下一層。這一層經過TCP協議保證了可靠的傳輸
  • 網絡層拿到TCP傳輸段以後,使用IP協議進行處理,加上IP頭,封裝成包進一步傳給下一層。這個IP決定了什麼路由或者主機須要接收處理這個包;
  • 數據鏈路層拿到網絡層的包以後,進一步封裝成數據幀,最終經過數據鏈路層進行發送處理,最終數據幀經過物理層傳輸給接收端。

1.3.2.二、分用

所謂分用,指的是主機或者中間設備接收到一個物理層傳輸過來的數據幀時,數據開始從協議棧中由底向上升,逐層處理,每層去掉對應的協議的報文首部。每層協議盒都要檢查報文首部的協議標識進行對應的協議處理。

image-20200726223316970

在上圖中,右邊部分爲分用的過程:

  • 主機B接收到物理層傳過來的數據幀以後,首先從首部找到MAC地址,判斷是否發送給本身的,若是不是則進行丟棄;
  • 若是發送包是本身的,則從數據幀肯定數據協議類型,再傳給對應的協議模塊,如IP、ARP等;
  • IP模塊接收到數據後獲取IP首部判斷首部接收的IPIP地址匹配,若是匹配則根據首部協議類型轉發給對應的模塊,如TCP、UDP等;
  • 傳到TCP模塊以後,首先TCP模塊會計算校驗和,判斷數據的完整性,而後處理數據包的順序接收相關邏輯;最後檢查端口號,肯定具體應該要轉發給應用層的哪一個應用程序。
  • 應用層接收到數據以後,解析數據進行展現,這裏是HTTP數據包,因此按照HTTP協議的約定進行解析展現。

能夠發現,以上流程中,你們感知最深入的就是傳輸層的TCP或者UDP協議,以及應用層的HTTP協議了,由於作網站開發,或者網絡通訊編程常常會用到它們的API。

上面介紹的並非很詳細,不過不要緊,後面咱們會把主要的協議都拿出來詳細的講解。

固然,正常的網站請求中,中間確定會涉及到不少路由器,交換機,光纖等底層的物理設備,中間會產生不少的逐跳(Hop-by-hop),每一箇中間系統都會對數據幀進行分用和封裝的過程。

1.四、TCP/IP協議簇

TCP/IP協議簇內容很是多,這裏列出的是本文可能會介紹到的相關協議,以及他們之間的交互關係:

image-20200726204748249

二、物理層

咱們的數據幀到底是怎麼傳給不一樣的主機呢。前面咱們瞭解到每個上層都依賴於下層的API,而物理層是最底層的了,它是真的要把數據傳出去了。而數據最終都會變爲0和1,物理層依賴於各類不一樣硬件技術,經過網絡的電子傳輸技術,把0和1在傳輸介質中進行傳輸。

2.一、通訊系統的模型

下我咱們舉一個最簡單的例子來講明通訊系統的模型[1]

好久之前,有些同窗家裏都是用的電話線進行上網的,這種網絡傳輸模型相似以下這樣:

image-20200712230429639

如上圖,主要包括源系統,傳輸系統,目的系統,能夠抽象爲下半部分的模型:

  • 源點:源點產生要傳輸的數據;
  • 發送器:源點產生的數據通過發送器編碼以後進行傳輸;
  • 傳輸系統:傳輸系統多是簡單的傳輸線,也多是複雜的網絡系統;
  • 接收器:接收傳輸系統的信號,轉換爲可以被目的設備處理的信息;
  • 終點:從接收器獲取傳送過來的數字比特流,最終輸出信息。

2.二、物理層解決什麼

傳輸媒介的種類很是多:雙絞線、對稱電纜、同軸電纜、光纜、無線信道等,致使物理層的協議種類較多。

物理層的主要做用是屏蔽掉這些傳輸媒介和通訊手段的差別,使物理層上面的數據鏈路層感受不到這些差別。爲此,物理層須要處理如下事情

  • 規定接口所用接線器的形狀和尺寸,引腳數目和排列,固定和鎖定裝置等;
  • 規定接口電纜各條線上的電壓範圍;
  • 規定某一電平電壓的意義;
  • 規定不一樣功能的各類可能事件出現順序。

2.三、物理層也出面試題?

最後我列幾個物理層常見的面試題,通常的開發人員都是工做在傳輸層以上,因此考一些TCP,UDP,HTTP,HTTPS等協議我以爲更貼近開發人員真實的工做場景。固然,若是是通訊領域的工程師,物理層都是屢見不鮮,這些但是通訊的基礎知識。即便知識應用開發工程師,瞭解這些也不會吃虧,說不定哪天親戚還須要叫你幫忙拉網線呢。

下面是幾個常見的物理層面試題:

有哪些通訊交互方式?單工、半雙工通訊、全雙工通訊?

單工通訊,又稱爲單向通訊,只有一個方向的通訊,如無線電廣播,電視廣播;

image-20200712122203747

半雙工通訊,又稱爲雙向交替通訊,雙方均可以收發信息,只能交替進行;

全雙工通訊,又稱爲雙向同時通訊,雙方能夠同時發送和接收數據。

image-20200712122339778

爲了提升信道利用率,有哪些信道複用技術?

所謂信道複用技術,指的是你們共享一個信道進行通訊,在接收端在使用分用器,把合起來傳輸的信息分別送到相應的終點;

頻分複用

用戶在分配到必定的頻帶後,通訊過程當中使用都佔用這個頻帶;

image-20200712123343946

時分複用

將時間劃分爲一段段等長時分複用幀,每個時分複用的用戶週期性的佔用幀位;

image-20200712123728888

統計時分複用

時分複用,若是用戶沒有任何數據要傳輸,也會週期性的給他分配時隙,這就致使了信道利用率不高。

爲此出現了統計時分複用。

統計時分複用使用STDM幀來傳送複用的數據,把全部用戶數據按時間順序組成STDM幀,放入一個隊列中,依次發送出去,這樣就可以更合理的共享信道。STDM幀中的數據須要添加用戶地址首部信息,以便可以正確的分發給目標用戶:

image-20200712130521935

這裏的集中器也叫智能複用器。

除了以上三種,還有波分複用和碼分複用,感興趣的朋友能夠自行搜尋資料瞭解,這裏就不繼續展開來說了。

物理層要解決什麼問題?

這個問題上一小節已經回答了。

2.四、物理層設備之集線器

若是咱們只是想用幾臺電腦搭建一個局域網,那麼能夠經過集線器(Hub)進行搭建,這個硬件工做在物理層,會把本身收到的字節都複製到其餘端口,以下圖:

image-20200712162725654

如上圖,其中一臺電腦發送信息以後,Hub以廣播的方式發給其餘三臺機器,可是究竟哪臺電腦纔會把消息接收下來呢?這裏咱們就要講到數據鏈路層了,在這一層判斷數據包是否是本身的。

三、數據鏈路層

3.一、數據幀格式

咱們首先來看看數據鏈路層的傳輸數據幀的格式。

全部的以太網(802.3)幀都基於一個共同的格式。在原有規範的基礎上,幀格式已被改進以支持額外功能。

當前以太網的幀格式[2]以下:

image-20200715085955337

前導:用在發送方和接收方之間同步時鐘和bit流;

SFD:幀開始界定符,只有一個byte,內容固定爲:10101011 (0xAB);

DST:目標MAC地址;

SRC:源MAC地址;

長度或類型:0800時,表示IP數據報,0806表示ARP請求/應答,0835表示RARP請求/應答;

FCS:幀檢驗序列,用於數據幀的差錯檢測;

這個包應該發給誰?

判斷是否應該接受這個包,就是經過幀的MAC地址進行判斷的。

這是一個物理地址,叫作鏈路層地址,由於鏈路層主要解決媒體接入控制問題,因此稱爲MAC地址(Media Access Control Address)。實際上,MAC地址就是適配器地址或適配器標識符,當適配器插入到某臺計算機以後,適配器上的標識符就成爲這臺計算機的MAC地址了。

怎麼校驗包是否出現錯誤

FCS是幀校驗序列,也就是循環冗餘檢測,收到數據報以後,會經過一個檢驗計算規則,把計算結果與FCS字段匹配,若是匹配補上,則幀可能在傳輸過程當中受損,一般會丟棄該幀。

3.二、ARP: 如何獲取目標機器的MAC地址?

咱們知道,在數據鏈路層,是經過MAC地址判斷某一個接收到的包是否是要進一步處理的。可是若是咱們不知道對方的MAC地址的時候,如何發送數據鏈路層的幀呢?這就須要用到數據鏈路層的ARP協議了。

ARP協議:ARP爲IP地址到硬件地址之間提供了動態映射,咱們經過ARP能夠把32位的Internet地址轉換爲48位的MAC地址。另外,咱們能夠使用RARP,把48位的MAC地址轉換爲32位的Internet地址。

image-20200713220351269

另外,爲了保證ARP的高效運行,ARP會維護每一個主機和路由器上的ARP緩存,把Internet地址和MAC地址的映射關係保存起來,緩存正常到期時間是20分鐘。

下面是這個過程的演示,其中ARP數據幀只把關鍵信息描述出來了,想要了解完整的幀格式能夠用參考 TCP/IP協議詳解卷1[3]

image-20200713231114251

如上圖:

主機A想知道192.168.1.4這個IP地址的MAC地址是什麼,發現本地緩存中找不到,因而廣播了一個ARP請求,主機B和主機D收到以後,發現本身不是192.168.1.4因而忽略這個消息,主機C發現本身就是192.168.1.4,因而響應了一個ARP數據幀。最終主機A收到主機C響應的數據幀,拿到了MAC地址,並把IP地址和MAC地址映射關係保存下來。

3.三、鏈路層設備之交換機

3.3.一、爲何須要交換機?

前面咱們用了集線器組件網絡,這個時候全部消息都會廣播到其餘端口,能夠發現集線器轉發了不少沒必要要的消息,能不能只發給須要的端口呢?這個時候就須要用到交換機了。

當一臺電腦A向交換機發送數據時,交換機會把電腦A的IP和MAC地址記住,保存到一個轉發表中,若是轉發表中暫時找不到目標IP地址的MAC地址,那麼首先仍是會廣播消息,最終轉發表會記錄全部請求過交換機的電腦IP和MAC。固然,轉發表也是有過時時間的。

image-20200713232323689

如上圖,看到交換機的奸笑沒有,與集線器不一樣,交換機是有靈魂的的,你告訴他你的身份證號和住址了,他就會偷偷記下來。

3.3.二、爲何有了IP地址,還須要有MAC地址?

IP地址是工做在網絡層的,後面會講到;

MAC地址是工做在數據鏈路層的,也就是交換機這一層,交換機之間的主機進行通訊,都是用的MAC地址,可是一旦走出了局域網,咱們就得用你們都公認的IP地址了。

MAC地址就好像是咱們的身份證,IP就像是咱們的住址,能夠根據住址寄送快遞,可是不能根據身份證號碼寄快遞,別人不知道怎麼走呢。

一個局域網,用身份證沒有問題呀,由於要找某我的,ARP會大喊一聲名字,那我的就會告訴你他的身份證號碼了,這個時候直接以身份證做爲標識傳消息,別人聽到不是本身的身份證就無論了。最終交換機這個小管家記住了全部人的名字跟身份證號碼,就會使用悄悄話的方式傳達消息了。這也就是用到了交換機的轉發表。

3.3.三、交換機拓撲環路問題

假設我如今拉網線,搞了一個這樣的拓撲結構:

image-20200714225908316

如上圖,主機準備發送一個消息出去,結果交換機B收到後,複製數據幀,發送給了交換機A、C、D,此時交換機B認爲主機是在左邊。可是不妙的事情發生了,交換機D收到消息後,因爲轉發表仍是空的,又是也複製數據幀,轉發到了交換機A、B、C,這個時候交換機B發現怎麼主機的數據又從右邊傳過來了,這些完全暈了,不知道主機究竟在哪裏。就這樣數據一致在這個網絡裏面打轉,這就拓撲環路致使的問題。

生成樹協議

爲了解決以上問題,因而 有了生成樹協議(Spanning Tree Protocol,STP)。

STP經過在每一個交換機禁用某些端口工做,來避免拓撲環路,保證不會出現重複路徑。

STP會找到拓撲結構的一個生成樹,經過生成樹避免環路。生成樹的造成和維護有多個網橋完成,在每一個網橋上運行一個分佈式算法。

以上拓撲,結構,最終應用了生成樹,禁用一些端口以後,可能會是這樣:

image-20200714225935282

這樣,消息就不可能再傳回左邊的主機了,從而避免了拓撲環路致使的問題。

創建生成樹:

網橋會發送一種稱爲網橋協議數據單元(BPDU)的幀來輔助造成和維護生成樹。

STP首先會嘗試選舉根網橋,根網橋是在網絡中標識符最小的網橋(也就是說優先級與MAC地址結合),網橋初始化的時候,假設本身是最小的網橋,而後用本身的網橋ID做爲根ID字段的值發送配置BPDU消息,若是發現ID更小的網橋,那麼會中止發送本身的幀,並基於接收到的ID更小的幀構造下一步發送的BPDU消息。發出根ID更小的BPDU端口被標記爲根端口,剩餘端口被設置爲阻塞或者轉發狀態。

3.四、VLAN

三、網絡層

網絡層,Internet layer,最熟知的就是IP協議了(Internet Protocol)。

前面咱們將的數據鏈路層,其實只能在局域網內進行通訊,由於都是經過MAC地址進行傳達信息的,要想跨局域網,那麼就得用到IP地址了,這就是網絡層要作的事情了。

首先咱們來介紹下網絡的一個協議:ICMP協議。

3.一、ICMP協議

IP協議自己不支持發現發往目的地地址失敗的IP數據包,也沒有提供直接的方式獲取診斷信息,好比在發送途中,通過了哪些路由器,以及往返時間。

爲此,就有了ICMP協議Internet Control Message ProtocolICMP)專門來負責這些事情。

ICMP並不爲IP網絡提供可靠性,它只是用於反饋各類故障和配置信息。丟包不會觸發ICMP。

ICMP是RFC 792中定義的Internet協議套件的一部分。ICMP消息一般用於診斷網絡或探測網絡目的,或者是爲了響應IP操做中的錯誤而生成(如RFC 1122中所指定),ICMP錯誤響應給原始數據包的源IP地址。

可是黑客常常用ICMP來作壞事,因而網絡管理員可能會用防火牆阻止掉ICMP報文,這樣的話,不少ping、traceroute之類的診斷程序就沒法正常工做了。

3.1.一、格式

ICMP報文是在IP數據報內部傳輸的,格式以下:

image-20200715084813447

而ICMP報文的格式以下:

image-20200715085522615

其中:

  • 類型有15個不一樣的值,描述特定類型的ICMP報文;
  • 某些ICMP報文仍是用代碼字段的值來進一步描述不一樣的條件;
  • 校驗和字段用於ICMP報文的差錯檢查。

如下是常見的差錯報文類型:

類型 代碼 描述 查詢 差錯
0 0 回顯應答(ping應答)
3 目標不可達
0 網絡不可達
1 主機不可達
2 協議不可達
3 端口不可達
4 須要進行分片但設置了不分片比特
5 源站選路失敗
6 目的網絡不認識
7 目的主機不認識
8 源主機被隔離(做廢不用)
9 目的網絡被強制禁止
10 目的主機被強制禁止
11 因爲服務類型 T O S , 網 絡 不 可 達
12 因爲服務類型 T O S , 主 機 不 可 達
13 因爲過濾,通訊被強制禁止
14 主機越權
15 優先權停止生效
4 0 源端抑制
5 重定向
0 對網絡重定向
1 對網絡重定向
2 對服務類型和網絡重定向
3 對服務類型和主機重定向
8 0 回顯請求(ping)
9 0 路由器通告
10 0 路由器請求
11 超時
0 傳輸期間生存時間爲0(Traceroute)
1 在數據報組裝期間生存時間爲0
12 參數問題
0 壞的 I P 首部(包括各類差錯)
1 缺乏必需的選項
13 0 時間戳請求
14 0 時間戳應答
15 0 信息請求
16 0 信息應答
17 0 地址掩碼請求
18 0 地址掩碼應答

其中,最經常使用的類型是8:回顯請求(ping),以及0:回顯應答(ping應答)。

3.1.二、查詢報文

查詢報文是有關信息採集和配置的ICMP報文。

咱們常常用到的ping程序就用到了ICMP查詢報文。

ping程序

ping程序會發送一份ICMP回顯請求給主機,並等待返回ICMP回顯應答。

ping程序ping不通了,就不能訪問對應的主機了嗎?

咱們知道,網絡管理員可能會用防火牆阻止掉ICMP報文的,這樣咱們可能就ping不通了,可是主機的可達性不能只取決於IP層是否可達,還與端口號和協議有關,而ping是運行在網絡層的,用於測試網絡鏈接狀態和信息包發送接收情況,即便ping不通,咱們也可能用telnet遠程登陸到主機的其餘端口,如25號端口。

ping程序用到了回顯請求和回顯應答報文,報文格式以下:

image-20200715233138672

Unix系統實現ping程序時,把ICMP報文的標識符設置爲進程ID,在進程內,序號從0開始,每發送一次新的 回顯請求就加1,這樣就能夠同時運行多個ping進程了。

ping程序的端口號是什麼?

端口號是傳輸層的東西,ping程序是使用ICMP協議,直接跳過了傳輸層,因此呢,ping程序是沒有所謂的端口號的。

咱們發送一個ping請求,數據在協議棧中的處理流程以下:

image-20200716085927626

  1. A主機的ping應用程序向服務器發起回顯請求,說了一句:hi
  2. 直接傳輸到網絡層的ICMP協議,進行ICMP數據封裝:
    1. image-20200716085942614
    2. 8表示回顯請求,112是發起請求的進程號,1表示請求序號
  3. IP協議拿到數據後進一步加上IP頭,加上本身的IP和目標IP,傳輸給數據鏈路層;
  4. 數據鏈路層拿到IP數據包,準備封裝成幀,這個時候會去尋找目標IP的MAC地址,若是在A主機的ARP映射表找到了IP的MAC地址,那就直接拿來用了,不然會發起一個ARP廣播請求,獲取到MAC地址。至於跨網關這種ping,會多了轉發的功能更,後面會進行介紹。最終數據鏈路層封裝成數據幀,從網絡接口發出去;
  5. 服務器拿到數據幀以後,拿到MAC頭,判斷MAC地址是本身的,就基於拿到Frame data,按首部協議傳給對應的模塊,即IP模塊;
  6. IP模塊拿到數據,判斷到IP跟本身對上了,與是繼續拿到IP data,傳輸給ICMP協議,ICMP協議收到消息,準備應答:
    1. image-20200716085953616
    2. 0表示回顯應答
  7. 而後按照一樣的流程,把數據包發送回A主機。

能夠發現,ping程序是直接用到了網絡層的ICMP協議,不通過傳輸層

是什麼緣由致使ping失敗了?

ping失敗的緣由有不少:

  • 多是輸錯了IP;
  • 多是網絡配置不正確,如錯誤的子網掩碼;
  • 可能有防火牆軟件組織了ping;
  • 多是硬件故障,如損壞了的以太網適配器,電纜,路由器,集線器等。

若是叫你本身實現一個ping程序,你會怎麼作呢?

提示:爲了能處理ICMP網絡報文,咱們須要用到原始套接字(SOCK_RAW),而不是SOCK_STREAM或者SOCK_DGRAM套接字。

更多提示:Homework 6: A raw socket ping tool[4],思路都在這裏了,你們動手作一作,而後就能夠有直接操做網絡層的工做經驗了。😏

3.1.三、差錯報文

差錯報文是有關IP數據報傳遞的ICMP報文。要是發送IP數據報中途產生了異常,那麼就會響應ICMP差錯報文。

可是不是全部狀況都會響應ICMP差錯報文,如如下場景:

  • ICMP差錯報文不會產生另外一個ICMP差錯報文;
  • 目的地址是廣播地址或者多播地址的IP數據報不會產生差錯報文;
  • 做爲鏈路層廣播的數據報不會產生差錯報文;
  • 源地址不是單個主機(源地址爲零地址、環回地址、廣播地址或者多波地址)的數據報不會產生差錯報文;

爲何要這些規則呢?假如容許ICMP差錯報文對廣播分組響應,那麼就會致使廣播風暴了。

下面咱們舉一個ICMP差錯報文的例子來講明下。

目標不可達

上面的表格咱們瞭解到,若是類型爲三則表示目標不可達,而根據具體的代碼能夠進一步劃分:

類型 代碼 描述 查詢 差錯
3 目標不可達
0 網絡不可達
1 主機不可達
2 協議不可達
3 端口不可達
4 須要進行分片但設置了不分片比特

下面咱們看一個端口不可達的例子來演示下ICMP差錯報文附加的信息。

ICMP端口不可達案例

這裏咱們演示經過tftp訪問一個不存在的端口號,查看其返回的ICMP響應差錯報文。tftp應用在傳輸層是經過UDP來進行傳輸數據的

下面咱們tftp請求以前先開啓tcpdump抓包:

sudo tcpdump -i en0 -nn host 個人IP and 目標IP

而後執行tftp命令:

tftp
tftp> connect 目標IP 8090
tftp> get test.foo
Transfer timed out.

能夠發現,在等待了大約25秒以後,最終輸出:Transfer timed out.

觀察tcpdump抓包日誌:

16:51:16.739632 IP 個人IP.64517 > 目標IP.8090: UDP, length 20
16:51:16.768590 IP 目標IP > 個人IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:21.743056 IP 個人IP.64517 > 目標IP.8090: UDP, length 20
16:51:21.751958 IP 目標IP > 個人IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:26.748387 IP 個人IP.64517 > 目標IP.8090: UDP, length 20
16:51:26.757631 IP 目標IP > 個人IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:31.752851 IP 個人IP.64517 > 目標IP.8090: UDP, length 20
16:51:31.794217 IP 目標IP > 個人IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:36.755172 IP 個人IP.64517 > 目標IP.8090: UDP, length 20
16:51:36.774400 IP 目標IP > 個人IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56

能夠發現這裏執行了五次UDP請求,每次請求都響應了一個ICMP包,爲udp port 8090 unreachable端口不可達,產生了ICMP不可達報文,該報文通常格式以下:

image-20200718171533801

爲何須要返回IP首部:由於IP首部包含了協議字段,使得ICMP知道如何解釋後面的8個字節;

爲何須要原始IP數據報中數據的前8個字節:由於這裏麪包含了源端口和目的端口。

不過看起來個人電腦好像忽略了ICMP報文,仍是繼續重試了4次。

注意:ICMP報文是在主機之間交換的,網絡層的協議,不須要端口號,而以上20個字節的UDP數據報是包含了源端口號和目標端口號信息的。

爲何TFTP客戶程序會繼續重發呢?

由於網絡編程中,BSD系統不把從socket接收到的ICMP報文中的UDP數據通知用戶進程,除非該進程以及發送了一個connect命令給該接口。標準的BSDTFTP客戶程序並不發送connect命令,因此它永遠也不會受到ICMP差錯報文的通知。

traceroute程序

traceroute工具用於肯定從發送者到目的地路徑上的路由器。

traceroute主要是經過故意設置特殊的TTL,來達到追蹤目的地路徑上的路由器的功能。

TTL運行原理

TTL:是 Time To Live的縮寫,該字段指定IP包被路由器丟棄以前容許經過的最大網段數量。每通過一個路由器,TTL就會減一,而後再把IP包轉發出去,若是TTL減到0了,路由器就會丟棄收到的TTL=0的IP包,並向IP包的發送者發送一個ICMP差錯報文,類型爲11,代碼爲0:傳輸期間生存時間爲0。

第一輪,traceroute設置TTL值爲1,那麼遇到第一個路由就返回ICMP容錯報文了,下一輪,TTL設置爲2...這樣依次增長。最終就把整個鏈路的路由器都試出來了。

固然,有點路由器不會回整個ICMP,這也是爲何你去traceroute一個公網地址,看不到中間路由的緣由。

除此以外,traceroute也能夠經過不設置分片,來肯定傳輸鏈路的MTU(Maximum Transmission Unit, 最大傳輸單元):首先發送一個分組的長度正好與出口MTU相等,若是中間遇到窄點的關口,就被卡主了,這個時候會接收到一個ICMP差錯報文,而後調小分組長度重試...

3.二、網關與路由器

在講數據鏈路層的時候,咱們用一個交換機,就構建了一個局域網。可是如今咱們局域網裏面的一臺機器,想要訪問另外一個局域網的機器,怎麼辦呢。這就是本節討論的內容。

咱們必須先鏈接下IP協議。

3.2.一、IP協議

IP是TCP/IP協議簇中最核心的協議,全部TCP、UDP、ICMP等數據都已IP數據報格式進行傳輸。

3.2.1.一、IP協議特色

  • IP協議是不可靠的傳輸協議,上一級咱們講到到了ICMP協議,每當傳輸出現異常,IP層都會丟棄數據包,而且可能會響應一個ICMP差錯消息給發送端,而任何要求的可靠性必須由上層如TCP協議來提供
  • IP協議是無鏈接的,也就是說IP不維護任何關於後續數據報的狀態信息,每一個數據報相互獨立。具體表如今:能夠不按發送順序接收,不用維護鏈接狀態,免去了維護複製的連接狀態信息(後面講傳輸層的TCP協議的時候會介紹到)。

3.2.1.二、IP數據報格式

下面是IP數據報的格式:

image-20200719221922958

  • 版本:協議版本號,指明IPv4仍是IPv6;
  • 頭部長度:最長60個字節;
  • 服務類型:包含3bit優先權子字段(已被忽略),4bit TOS子字段(分別表明最小時延、最大吞吐量、最高可靠性和最小費用)和1bit未用位但必須置0;
  • 總長度:指的是整個IP數據報的長度,單位字節;
  • 標識符:惟一地標識主機發送的每一份數據報,一般每發送一個數據報就+1;
  • 標誌:主要用於IP分片;
  • 分片偏移:主要用於IP分片;
  • 生存期:設置數據報能夠通過最多的路由器數;
  • 協議:主要代表IP數據是什麼協議,用於對數據報進行分用;
  • 頭部校驗和:校驗數據報是否正確;
  • 源IP地址:發送IP數據報的IP地址;
  • 目的IP地址:IP數據報目的IP地址;
  • 選項:可選數據;
  • IP數據:具體的IP數據;

3.2.二、路由器

路由器通常充當一個網關,屬於三層設備。會把MAC和IP頭取下來根據內容進行處理。路由器有五個網口,分別能夠鏈接5個局域網,每一個網口和局域網的IP地址相同的網段,每一個網口都是對應的局域網的網關。

5個網口中通常包含一個外網網口,外網網口用於鏈接到WAN上。

路由器除了有交換機的功能外,更擁有路由表做爲發送數據包時的依據,在有多種選擇的路徑中選擇最佳的路徑。

一層設備、二層設備、三層設備分別有什麼區別?

路由器是屬於OSI第三層的產品,交換機是OSI第二層的產品。

第二層的產品功能在於,將網絡上各個電腦的MAC地址記在MAC地址表中,當局域網中的電腦要通過交換機去交換傳遞數據時,就查詢交換機上的MAC地址表中的信息,將數據包發送給指定的電腦,而不會像第一層的產品(如集線器)每臺在網絡中的電腦都發送。

而路由器除了有交換機的功能外,更擁有路由表做爲發送數據包時的依據,在有多種選擇的路徑中選擇最佳的路徑。此外,並能夠連接兩個以上不一樣網段的網絡,而交換機只能鏈接兩個。路由表存儲了(向前往)某一網絡的最佳路徑、該路徑的「路由度量值」以及下一個(跳路由器)

網關地址通常是網段的第一個或者第二個,如192.168.23.0/24這個網段,網關地址多是192.168.23.1/24或者192.168.23.2/24。

在不一樣的局域網中,私有IP地址是會重複的,而咱們要訪問公網的時候,必定要分配一個共有IP地址,因此,咱們在訪問公網的時候,須要路由器幫忙把私有IP變爲共有IP,這種叫作NAT網關,普通內網之間的通訊用到的稱爲轉發網關。

咱們先來看看轉發網關

3.2.2.一、轉發網關

假設主機A和主機B屬於同一個內網,他們經過兩個路由器鏈接起來,以下圖:

image-20200719171542654

主機A要訪問主機B,流程以下:

  • 主機A發現要訪問的主機B不是在同一個網段,準備先找到網關,把消息發給網關,網關地址是192.168.1.1,主機A經過ARP獲取到了網關的MAC地址,而後發送以下數據包:

    • SRC MAC: 主機A的MAC
      DST MAC: 路由器A的192.168.1.1網口的MAC
      SRC IP : 192.168.1.3
      DST IP : 192.168.3.4
  • 路由器A的192.168.1.1網口接收包以後,準備把包轉發出去。而路由器A中的路由表中匹配到了,要想發送給192.168.3.4/24,須要從192.168.2.1這個網口出去,下一跳地址爲192.168.2.2/24。路由器經過ARP拿到了下一跳192.168.2.2/24d的MAC地址,而後發送以下數據包:

    • SRC MAC: 路由器A的192.168.2.1網口的MAC
      DST MAC: 路由器B的192.168.2.2網口的MAC
      SRC IP : 192.168.1.3
      DST IP : 192.168.3.4
  • 路由器B的192.168.2.2網口接收包以後,準備把包轉發出去。路由器B中判斷到目標IP在192.168.3.1這個網口所在的局域網,因而經過ARP拿到了192.168.3.4的MAC地址,而後發送以下數據包:

    • SRC MAC: 路由器B的192.168.3.1網口的MAC
      DST MAC: 主機192.168.3.4網口的MAC
      SRC IP : 192.168.1.3
      DST IP : 192.168.3.4

最終,主機B收到數據包。

能夠發如今轉發網關中,源IP和目的IP地址都是不會變的,由於整個內網不可能有衝突的IP

可是,假如咱們要訪問外網,狀況就不同了,最終可能會請到到另外一個局域網,另外一個局域網的私有IP是可能跟咱們所在的局域網同樣的,爲了不衝突,因而就有了NAT網關。專門在把數據包發送出去以前,把IP改成公網IP。

3.2.2.二、NAT網關

如今假設主機A要訪問另外一個城市的主機B,這裏爲了演示NAT,咱們把模型簡化一下,假設路由器出去以後就是公網IP了,以下:

image-20200719184500782

假設路由器A和路由器B都直接接入了互聯網。

如今主機A想訪問主機B:

  • 因爲是不一樣的局域網,主機A不會知道主機B的IP的,而主機B接入互聯網的以後,領取到了一個互聯網的IP,就是上圖路由器WAN口的IP:203.0.113.103,因此主機B會把這個IP做爲主機B的IP,最終發出以下IP數據包:

    • SRC MAC: 主機A的MAC
      DST MAC: 路由器A的192.168.1.1網口的MAC
      SRC IP : 192.168.1.3
      DST IP : 203.0.113.103
  • 192.168.1.1網口接收到包以後,發現要想訪問203.0.113.103,就要從203.0.113.102這個網口出去,發給路由器B,路由器B中判斷到目標IP就是203.0.113.103這個網口,因而經過ARP拿到了203.0.113.103的MAC地址,而後發送以下數據包:

    • SRC MAC: 路由器A的203.0.113.102網口的MAC
      DST MAC: 路由器B的203.0.113.103網口的MAC
      SRC IP : 203.0.113.102
      DST IP : 203.0.113.103
    • 由於消息是要發到公網的,最終SRC IP會被NAT爲公網的IP 203.0.113.102;

  • 最終路由器B接收到消息,經過NAPT獲得最終接收數據報的IP爲當前局域網的192.168.1.3/24,最終把消息轉發給了這個IP所在的主機B。

NAPT是如何把一個公網IP翻譯爲局域網IP的?

傳統的NAT(traditional NAT)包括基本NAT(basic NAT)和網絡地址端口轉換(Network Address Port Translation, NAPT)。基本NAT只執行IP地址的重寫,本質上是將私有地址改寫爲一個公共地址,這每每取自於一個由ISP提供的地址池或共有地址範圍,這種NAT不是最流行的,由於無助於減小須要使用的IP地址數量。

比較流行的作法是使用NAPT,NAPT使用傳輸層標識符如TCP或者UDP端口,或者ICMP查詢標識符來肯定一個特定的數據報到底和NAT內部哪臺私有主機相關聯。

若是局域網兩個端口號同樣,那麼NAPT會重寫端口號,保證不一致。以下圖,三個局域網的IP須要轉換爲公網IP,因爲有兩個的端口重複了,因而NAPT進行了端口重寫:

image-20200719184357681

3.三、路由策略

3.3.一、靜態路由

咱們經過route命令和iproute命令均可以進行路由策略的配置和查詢。

  • 能夠指明去哪一個網絡,走哪一個網口,網口的IP是什麼;
  • 也能夠建立不一樣的路由表,針對不一樣的請求來源,走不一樣的路由表配置;
  • 固然,也能夠按照權重給下一跳地址走配置;
  • 同一個路由,也能夠配多個運營商的網絡,針對不一樣的IP,採用不一樣的運營商網絡
  • ...

配置時很是靈活的,可是在複雜的網絡環境下手動配置路由成本太大了,而且網絡結構也是常常發生改版的。

因此,咱們能夠使用動態路由路由器,這種路由器會根據路由協議算法生成動態路由表,動態的隨着網絡運行情況調整路由表。

3.3.二、動態路由協議

網絡是複雜的,爲了生成動態的路由表,須要配合特定的算法,主流的動態路由主流有兩種算法。

內網路由協議

基於鏈路狀態算法實現的OSPF協議(Open Shortest Path First, 開放式最短路徑優先):主要用於數據中心內部,所以也成爲內網路由協議(Interior Gateway Protocol,IGP),關鍵是找到最短的路徑。

OSPF是一種鏈路狀態路由協議。能夠將其視爲網絡的分佈式地圖。

外網路由協議

基於距離矢量算法實現的BGP協議(Border Gateway Protocol,外網路由協議):距離矢量,就是每一個路由器都保存一個路由表,路由表每行保存了下一跳的路由器,以及距離下一跳路由器的距離。也成爲邊界網關協議。

在BGP的世界中,每一個路由域都稱爲自治系統或AS。BGP所作的工做一般是經過選擇遍歷最少自治系統的路由:最短的AS路徑來幫助選擇經過Internet的路徑。

咱們會把重點放在傳輸層以上,因此動態路由協議這部分咱們暫時不作不深刻研究。

四、傳輸層

傳輸層涉及到兩個重要的協議:UDP和TCP,本節咱們重點介紹這兩個協議。

4.一、UDP協議

4.1.一、UDP數據報格式

UDP基本沒幹啥事,繼承了IP包的特性:數據可能丟失,順序傳輸沒法保證。UDP與後邊介紹的TCP不同,是無狀態的。咱們來看看UDP數據報的格式:

image-20200719222327297

  • 源端口號:發送數據報方使用的端口號,用於標識發送進程;
  • 目的端口號:接收數據包方使用的端口號,用於標識接收進程;
  • UDP長度:UDP頭部和UDP負載數據的字節長度;
  • UDP校驗和:UDP校驗和覆蓋UDP頭部和UDP數據和一個僞頭部(區別:IP頭部校驗和只覆蓋IP頭部),僞頭部衍生子IPv4頭部字段的12個字節,或者衍生子IPv6頭部字段的一個40字節的僞頭部;
  • 負載數據:具體的UDP數據。

能夠發現,UDP與下層不一樣,是須要端口號的。

爲何UDP須要端口號,TCP和UDP端口號能夠相同嗎?

相似ICMP協議回顯請求的標識符,UDP的端口用於區分是哪一個進程的數據包,若是沒有端口號,那麼就不知道應該把數據包最終交給哪一個進程來處理了。

TCP端口號由TCP來查看,UDP端口號由UDP來查看,TCP端口號和UDP端口號是相互獨立的,因此是能夠相同的。每一個請求都有源IP、目標IP、源端口號、目標端口、協議五個元素來標識的,每一個協議的端口池是徹底獨立的。

爲何UDP的端口號最可能是65535個?

在UDP/TCP協議中源端口和目的端口都只有16位,也就是說端口的取值範圍爲0~65535。

4.1.二、UDP特色

UDP在IP層之上,沒有作其餘的封裝,主要表現以下特色:

  • 數據可能丟失,順序傳輸沒法保證;
  • 無狀態,不須要像TCP那樣要創建鏈接;
  • 沒有擁塞控制,來一個包就發一個。

4.1.三、UDP使用場景

基於UDP的特色,UDP主要用於如下場景:

  • 須要資源少,在網絡狀況比較好的內網,或者對對包不敏感的場合。如DHCP和TFTP就是基於UDP的;
  • 廣播場景,不須要一對一創建鏈接,如DHCP;
  • 須要時延低,容許丟包,不關注網絡擁塞的場景,如視頻直播這種流媒體,實時遊戲,通訊,物聯網等領域。

4.二、TCP協議

TCP是咱們平時用到最多的協議,特別是作web開發的時候,或者互聯網後端開發,真的是時時刻刻都會用到,這裏我會展開來說。《TCP/IP詳解-卷1:協議》一書中花了6章來說解TCP的各類功能,單單是從TCP/IP協議棧的名稱就能夠看出,TCP協議的份量有多重了。爲此,面試官張口就聊TCP咋的咋的。

img

與UDP不一樣,TCP作了不少功能的封裝與實現。

先來簡單介紹下TCP協議

TCP給應用程序提供給了一種與UDP徹底不一樣的服務。

TCP是面向鏈接的可靠的服務面向鏈接指TCP的兩個應用程序必須在它們可交換數據以前,經過相互聯繫來創建一個TCP鏈接;

TCP提供了一種字節流抽象概念給應用程序:TCP不會自動插入記錄標誌或者消息邊界,這意味着TCP沒有限制應用程序的寫範圍。發送端分兩次發10字節和30字節,接收端可能會以兩個20字節的方式讀入。

咱們仍是先來看看TCP數據報的格式吧,這個可比UDP複雜多了,可是也是設計的恰到好處的。

4.2.一、TCP數據報格式

image-20200721222551224

如上圖,頭部深黃色部分爲TCP特有的重點字段,後面TCP相關功能基本都是靠這些特有的字段來實現的。

  • 源端口號和目的端口號:同UDP同樣,主要用於區分數據應該轉發給哪一個應用;
  • 序號:這個序號是爲了解決亂序問題,32位無符號數,到達2^32-1後再從新從0開始;
  • 確認號:確認已經接收到了哪裏,該確認序號表示該確認號的發送方指望接收的下一個序列號。該字段只有在ACK位字段被啓用的狀況下才有效,因此也成爲ACK號或者ACK段;
  • 狀態位:該狀態位會讓TCP鏈接雙方的狀態發生流轉,常見的狀態爲,後面講創建鏈接和斷開鏈接的時候會用到:
    • ACK:回覆狀態,啓用該狀態的狀況下,確認號有效,鏈接創建以後通常都是啓用狀態;
    • SYN:發起一個鏈接;
    • RST:重置鏈接,鏈接去表,常常是由於錯誤致使;
    • FIN:結束鏈接,表示該報文的發送方已經結束想對方發送數據;
    • CWR:擁塞窗口減少,發送方下降發送速率;
    • ECE:ECN回顯,發送方接收到了一個更早的擁塞通告;
    • URG:緊急,表示緊急指針字段有效,不多用到;
    • PSH:推送,表示接收方應該儘快給應用程序傳送這個數據——沒有被可靠的實現或用到;
  • 窗口大小:流量的窗口大小,用於流量控制,通訊雙方各聲明一個窗口,這個大小代表了本身當前的處理能力;
  • 校驗和:覆蓋了TCP的頭部和數據,以及僞頭部數據(與UDP使用的類似的僞頭部進行計算);
  • 緊急指針:只有在URG位啓用的時候纔有效;
  • 選項:如最大段大小等其餘的可選項;
  • 數據:TCP數據報的數據內容。

4.2.二、TCP特色

TCP基於以上數據報的各類字段,實現瞭如下功能:

  • 數據的順序傳輸;
  • 丟包重傳,保證可靠;
  • 鏈接維護;
  • 流量控制,保證穩定;
  • 擁塞控制,及時調整,最大程度保證傳輸正常進行。

4.2.三、鏈接管理

咱們首先來看看鏈接是如何創建的,這裏就涉及到TCP的三次握手了。

4.2.3.一、TCP三次握手

三次握手流程以下:

image-20200723224535183

能夠發現,爲了實現可靠鏈接,雙方都須要發起創建鏈接。具體流程以下:

  • 第一次握手:主動鏈接方發送一個SYN報文段指明本身想要鏈接的端口號,以及客戶端消息的初始化序列化ISN(c);
  • 第二次握手:服務器接收到消息後,也發送本身的SYN報文,包含了服務端的初始化序列號ISN(s),並設置確認號ack=客戶端序列號+1;
  • 第三次握手:客戶端應答服務器的SYN,將服務端的序列號+1做爲ack返回給服務端。

總結一下:客戶端與服務端利用SYN報文交換彼此的初始化序列號。在咱們熟悉的Socket編程中,三次握手在執行connect的時候觸發。

其中的ACK應答和遞增的序列化是可靠性的保證。

爲何是三次握手,而不是兩次或者四次?

若是是兩次:

image-20200723001805421

客戶端請求創建鏈接,服務端收到了請求,而且作出了響應,很明顯,服務器無法知道這個響應究竟有沒有被接收,也許可能客戶端遲遲收不到SYN響應,因而結束了請求。這個時候再傳消息網絡層就會收到一個ICMP目的不可達的差錯報文。

同理:客戶端的SYN請求若是遲遲沒有服務器的響應,那麼也會重發SYN,最終若是服務端可能收到兩個SYN,客戶端想要創建一個鏈接,可是服務器收到兩個SYN以後,創建了兩個鏈接(固然,實際上的三次握手服務端是會判斷客戶端的請求序列號的,發現是同一個序列號,並不會創建多個鏈接,這也說明序列號的重要性)。

爲何不須要四次呢?由於若是服務端和客戶端雙方都發起SYN,而且收到ACK以後,就都知道對方接受了本身的請求了,已經沒有必要再繼續確認下去了。

爲何UDP端口號爲65535個?

在TCP、UDP協議的開頭,會分別有16位來存儲源端口號和目標端口號,因此端口個數是2^16-1=65535個。

4.2.3.二、TCP四次揮手

接下來咱們看看鏈接關閉的流程,鏈接的任何一方均可以發起關閉操做,此外,也支持雙方同時關閉鏈接。在傳統的狀況下,負責發起關閉鏈接請求的一般是客戶端。

這個流程又被稱爲四次揮手:

image-20200724000124983

  • 鏈接的主動關閉者發送一個FIN段請求關閉鏈接,攜帶了Seq=K,指明接收方但願看到的本身的當前序列號;攜帶了ack=L,指明本身想要接受到的下一個消息的序號。這個時候,鏈接主動關閉者代表了本身已經沒有數據要發送了,可是仍然能夠接受被動關閉者發送的數據;
  • 鏈接的被動關閉者進行了ACK迴應,ack爲K+1,代表本身已經成功接收到了主動關閉者發送的FIN。可是本身還未準備好關閉,因此主動關閉者會進入FIN_WAIT_2等待狀態;
  • 緊接着被動關閉者也發送了一個FIN端請求關閉鏈接,攜帶了Seq=L。告訴主動關閉者本身也準備好了關閉;
  • 最後鏈接的主動關閉者接收到了對方的FIN關閉請求,也迴應了一個ACK,一樣的ack=L+1,代表本身已經成功接收到了被動關閉者發送的FIN;

能夠發現,由於TCP是全雙工的,雙方都要單獨發起關閉請求,只有當鏈接雙方都發起FIN關閉請求操做,而且獲得確認以後,才完成一個完整的關閉操做,這也是被稱爲四次握手的緣由。

信息發送期間的狀態流轉如上圖所示。其中主動關閉者在CLOSED狀態以前,有一個TIME_WAIT狀態,那麼問題來了:

爲何要有TIME_WAIT狀態呢?

咱們知道主動關閉者在應道對方的FIN請求,有可能對方是收不到的,若是收不到的狀況下,那麼對方就可能認爲本身的FIN請求丟失了,須要從新發起FIN請求,因此主動關閉者須要有一個足夠長的等待時間,讓對方有重試的機會

等待時間是2MSL(Maximum Segment Lifetime,報文最大生存時間),這也是報文在網絡上最大的生存時間,超過了這個時間就會被丟棄。RFC 793中規定MSL爲2分鐘,實際應用中經常使用的是30秒,1分鐘和2分鐘等。若是超過了這個時間,那麼主動關閉者就會發送一個RST狀態位的包,表示重置鏈接,這個時候被動關閉者就知道對方已經關閉了鏈接:

image-20200724004705111

若是主動關閉者不進行等待,會出現什麼問題呢?以下:

image-20200724003739171

能夠發現,因爲端口複用,主動關閉者已經開啓了另外一個鏈接,這個時候被動關閉者還在重試發起FIN請求,致使新主動關閉者新的鏈接收到了不少沒用的包。由於包是有序列號的,因此能夠判斷到不是本次鏈接該接收的包。爲此,咱們須要讓主動關閉者進行等待,確保被動關閉者不會再發FIN請求了,再進行端口複用。

4.2.3.三、完整鏈接流程

完整鏈接流程以下:

image-20200724000001081

能夠發現,每一個TCP鏈接在正常的創建和關閉的基本開銷是7個報文段,若是隻是須要交換不多量的數據時,有寫程序更願意選擇使用UDP協議。可是UDP會面臨數據丟失,擁塞管理,流量控制等問題。

4.2.四、TCP狀態機

介紹了三次握手和四次揮手,咱們再看看看如下這個TCP狀態機就清晰多了。

若是沒有看過三次握手和四次揮手流程,不建議直接看這個狀態機,真的是太複雜了...不過爲了方便你們可以更直觀的看出狀態流轉,我仍是繪製了下,加了一些說明:

image-20200729233117353

4.2.五、數據傳輸

4.2.5.一、如何保證可靠傳輸:ACK+序列號

假設主機A經過TCP向主機B發送數據,當主機A的數據到達主機B時,主機B會發送一個確認應答消息ACK。主機A收到ACK以後,就知道本身的數據已經被對方接收了:

image-20200724081253191

若是主機一直沒有收到ACK,必定時間以後,就會重發,所以,即便主機A的數據報沒有發到主機B,或者主機B的ACK數據包丟失了,也有重傳機制,確保雙方最終能夠經過重傳確保可以正確收到消息:

image-20200724082856702

從上圖也能夠看出,主機A實際發了兩次一樣的數據給主機B,主機B能夠經過序列號,判斷是重複數據,而後就丟棄了,可是仍是會發送一個ACK告訴主機A已經收到消息。

4.2.5.二、流量控制與窗口管理

在TCP頭部中,爲了實現流量控制,包括順序問題與丟包問題,咱們重點關注TCP頭部的這三個字段:序列號,序列號與確認號:
image-20200725114826908

(注意:後面部分數據傳輸圖中的發送方統一稱爲客戶端或者發送端,接收方統一稱爲服務端或者接收端,實際的數據傳輸,能夠是兩臺電腦之間,或者是兩臺服務器之間)

其中TCP頭部的窗口字段代表本身的處理能力,表明着可用緩存空間的大小,以字節爲單位。

接下來再看看滑動窗口。

TCP鏈接的每一個端均可以收發數據,每一個端的收發數據量是經過一組窗口結構來維護的。每一個端都會包含一個發送窗口結構和接收窗口結構。

發送窗口結構

發送窗口結構以下圖所示:

image-20200725125106588

其中:

  • SND.WND:提供窗口大小是由接收返回的ACK中的窗口大小字段控制的;
  • SND.UNA:記錄窗口左邊界的值;
  • SND.UNA + SND.WND:記錄窗口右邊界的值;
  • SND.NXT:記錄下次發送的數據

所謂窗口,就是左右邊界會根據狀況進行調整的窗口,由主要三個動做:

  • 關閉:窗口左邊界右移,當已發送的數據獲得ACK的時候,就會進行關閉,提供窗口大小減少;
  • 打開:窗口左邊界右移,當已確認的數據獲得處理後,那麼接收端可用緩存就會變大,這個時候經過打開操做讓提供窗口大小變大;
  • 收縮:窗口右邊界左移,使得提供窗口大小減少;
接收窗口結構

接收窗口與發送窗口結構相似,以下圖:

image-20200725143723481

從滑動窗口看如何保證可靠傳輸:順序與丟包問題

爲了不接收重複數據:接收到的數據包小於左邊界,說明是已經確認過的,將把數據報丟棄;若是接收到的數據報序列號大於右邊界,說明暫時超出了處理能力範圍,也將會被丟棄。

爲了保證已確認數據包的連續性,接收到的數據包的序列號與已確認 已接受部分連續的時候,才表示真正的已確認,左邊界才能夠右移。

image-20200725222605426

4.2.5.三、超時重傳機制

基於計時器的重傳超時機制(Retransmission Ttimeout, RTO)

TCP在發送數據的時候會設置一個重傳計時器,若是計時器超時仍然沒有收到ACK確認信息,那麼會進行重傳操做。

以下圖是超時重傳的演示說明例子:

image-20200725230608270

對於接收方來講,1,2,3都已經接收而且發送ACK了,3的ACK丟失了。

ACK丟失的場景:過了一段時間,3的計時器發現超時了,因而會觸發超時重傳。可是這個時候接收方發現3是在已接受已確認區域,因而會丟棄3,並反饋一個ACK;

數據丟失的場景:4和5的數據傳輸丟失了,計數器發現超時,也會進行超時重傳,保證4和5能夠傳給接收方,並拿到ACK反饋。

關於重傳時間間隔

ICMP端口不可達案例中,採用UDP的TFTP客戶端使用簡單且低效的超時重傳策略:設置足夠大的超時間隔,每5秒進行一次重傳;

而TCP的基於計時器的重傳策略是若是發生重試,能夠有兩種處理方式:

  • 一種是基於擁塞控制機制,減少發送窗口大小;
  • 另外一種是超時時間間隔會一直加倍。

關於重傳時間

重傳時間須要講到自適應重傳算法,一種計算重傳時間的算法,大體流程:

TCP經過採樣RTT的時間,進行加權平均,算出一個值,最終獲得一個估計的重傳時間。

初始值:原始值
測量以後:RTO = RTTs + 4*RTTd
(RTTs:加權平均值,RTTd:誤差值)

由於網絡是不斷變化的,因此重傳時間也會處於變更狀態。

基於反饋信息的快速重傳機制

快速重傳機制是這樣的:當接收方接收到一個序列號大於下一個所指望的報文段的時候,就會檢測到數據流中間丟失的間隔,而後發送冗餘的ACK,向發送者索要確實的間隔。當發送者收到必定數量的冗餘的ACK(稱爲重複ACK的閾值或dupthresh)以後,就不等定時器過時了,直接重傳丟失的 報文。

重複ACK的閾值一般爲3,一些非標準化的實現可基於當前的失序程度動態調整。

以下例所示:發送方的四、五、六、7都已經發送出去了,可是接收方接收到了五、六、7,少了4,會在分別收到五、六、7的時候都發一個3的ACK,向發送方索要下一個數據4。這樣發送方就收到到3個3的ACK了,因而就主動發起了4的重傳,不等待重傳計時器超時了:

image-20200726104928030

帶選擇確認的重傳SACK

雖然重傳保證了數據的到達,可是重傳應該儘量保證不重傳以正確接收到的數據,而SACK信息能更快速的實現空缺填補而且減小沒必要要的重傳。

隨着選擇確認選項的標準化[RFC2018],TCP接收端能夠提供SACK的功能了,經過TCP頭部的累計ACK號字段來描述其接收到的數據。

每當緩存存在失序數據時,接收端就能夠生成SACK,表明着緩存接收狀態地圖,這樣經過將緩存的接收狀態地圖發給發送方,發送方就很快能夠知道是什麼數據丟失併發起重傳了。

這種重傳機制下,窗口內的其餘報文段也能夠被接收確認,但只有在接收到等於窗口的左邊界的序列號時,窗口才會前移。這樣就減小了窗口內的沒必要要的重傳。

4.2.5.四、流量控制

流量控制指的是經過控制發送方和接收方的窗口大小,以使得接收方緩存中已接受的數據處理不過來時,經過減少發送方的窗口大小,讓接收方能有足夠的時間來接收數據包;或者是接收方比較空閒時,嘗試讓發送方調大窗口大小,以加快傳輸,合理利用空閒的網絡資源。

流量控制主要是經過TCP頭的窗口大小來調節的。發送端收到接收端的通告窗口以後,得知接收端可接收的數據量。

下面舉例來講明。

正常狀況下,發送方左邊界每關閉一格,右邊界就打開一個,多一個可發送的單元:

image-20200726115726307

咱們知道接收端接收並確認數據以後,會放到緩存中,等待應用程序處理,若是應用程序一直沒有處理,最終會致使接收端沒有更多空間來存儲到達的數據了,若是應用程序一直沒有處理數據,那麼窗口右邊界可能就不會打開了,最終接收的窗口大小變爲0:

image-20200726121531246

這個時候接收端就會發送一個零窗口通告(TCP ZeroWindow),告知發送端不要再發送數據了,我已經處理不過來了,因而發送方就暫停發送數據了,等待接收端的窗口更新(TCP Window Update)通知:

image-20200726133315644

這樣,接收方就能夠有時間來處理接收的數據了,等到有了足夠多的緩存以後,因而會給發送端傳輸一個窗口更新通知。

爲了不因爲窗口更新通知ACK丟失,到時雙方陷入等待的僵局,在發送方中止發送數據以後,會採用一個持續計時器間歇性的查詢接收端,給接收端發送窗口探測(TCP ZeroWindowProbe)請求,要求接收端返回TCP ZeroWindowProbeAck,看看是否窗口是否已經增長了:

image-20200726133219069

4.2.5.五、擁塞控制

前面咱們講到,能夠經過滑動窗口大小來控制流量,從而爲接收方緩解壓力,避免沒必要要的丟包。

而擁塞控制,就須要用到擁塞窗口了。擁塞控制主要用於避免丟包和超時重傳。

反映網絡傳輸能力的變量稱爲擁塞窗口(congestion window),記爲cwnd。

能夠理解爲滑動窗口是爲接收方服務的,而擁塞窗口是爲整個網絡通道服務的,擁塞窗口大小又會受制於接收方滑動窗口大小,而且會由於網絡緣由進行調整。由於網絡通道中的任何一個環節都有可能影響總體的傳輸效率。

4.2.5.5.一、發送實際可用窗口

那麼咱們發送端實際可用窗口應該是多少了,這裏咱們記實際可用窗口大小爲W,那麼W爲接收端通知窗口awnd和擁塞窗口cwnd的較小者:

W = min(cwnd, awnd)

假設網絡沒有任何問題,而且帶寬足夠寬,數據包不會在傳輸過程遇到須要排隊等待的狀況下,這種理想情況下,也就是沒有網絡延遲,接收方收到一個數據包,馬上就ACK一個,馬上空出一個可傳輸單元,發送的實際可用窗口就是接受方的滑動窗口大小了,以下:

image-20200726144055451

理想是很美好的,可是實際網絡狀況是很是複雜的,TCP根本不知道里面會發生什麼狀況,也許W還沒到達接收端滑動窗口大小,網絡中就由於中間的瓶頸致使丟包了,那麼更加會增長重傳的頻率。因此爲了能減小丟包和超時重傳,須要有一些動態發送端窗口大小的策略。

4.2.5.5.二、發送端窗口調整策略

雖然能夠經過接收方的ACK獲得對方的接收窗口大小,可是由於剛開始並不知道擁塞窗口是多少,因此只能以愈來愈快的速率不斷髮送數據,直到出現數據包丟失爲止。

一般TCP在創建新鏈接的時候會執行慢啓動,直到有包丟失,然執行擁塞避免算法進入穩定狀態。

慢啓動

初始窗口設爲IW(Initial Window, IW),IW=SMSS(發送方的最大段大小)。

先發送初始窗口大小的數據,沒有出現丟包,而且每收到一個ACK,慢啓動算法就會以min(N,SMSS)來增長cwnd的值。可見這是指數性的增加。

直到出現了網絡擁塞,出現丟包、超時重傳,說明已經到達了慢啓動的閾值ssthresh(slow start threshold),這個時候cwnd減小一半,並做爲新的ssthresh。

避免擁塞

一旦達到慢啓動的閾值以後,爲了獲得更多的傳輸資源而不影響其餘鏈接的傳輸,TCP實現了擁塞避免算法。一旦肯定慢啓動閾值,TCP會進入擁塞避免階段,這個時候cwnd每次的增加值近似於成功傳輸的數據段大小。也就是說由原來慢啓動的指數增加,變爲了線性增加。

4.三、Socket編程

4.3.一、Socket是什麼

Socket是一個抽象層,主要是把TCP/IP層複雜的操做抽象爲幾個簡單的接口提供給應用層調用,進而實現應用進程在網絡中通訊。Socket主要是端到端之間的傳輸協議(網絡層之上的協議)。因爲Socket是一種高層的抽象網絡API,是一種端到端的通訊,只能訪問到端到端協議之上的網絡層和傳輸層

image-20200726175908288

Socket起源於Unix,在Unix中,一切皆文件,Socket也不例外,是一種打開-讀/寫-關閉的模式實現的。在服務器和客戶端各自維護了一個文件。

4.3.二、基於TCP的Socket通訊交互流程

咱們先來看一下基本TCP客戶/服務器程序的套接字函數調用過程:

image-20200726183837690

TCP Socket的文件結構

在內核中,Socket是一個文件,不過Socket對應的inode不是保存在硬盤上,而是在內存中,該inode指向了Socket在內核的Socket結構。內核的Socket接口主要由兩個隊列:發送隊列,接收隊列。

內核爲監聽套接字維護的兩個隊列

對於每一個監聽Socket,內核都爲其維護了兩個隊列:

  • 未完成隊列(incomplete connection queue):這個隊列的套接字服務端正在等待完成TCP三次握手,處於SYN_RCVD狀態;
  • 已完成鏈接隊列(completed connection queue):完成了三次握手的Socket鏈接會進入這個隊列,處於ESTABLISHED狀態。

以下圖:

image-20200726194117299

4.3.三、基於UDP的Socket通訊交互流程

UDP不須要三次握手,因此不須要listen和connect,可是交互仍然須要IP和端口號,須要bind。

UDP不用維護鏈接狀態,因此不須要針對每一個鏈接創建一組Socket,只須要一個就能夠了。

如下是UDP的Socket通訊交互流程圖:

image-20200726195423611

到目前爲止,咱們把物理層、數據鏈路層、網絡層、傳輸層主要的協議和功能都介紹了一遍。基於這些底層的協議棧支撐,咱們能夠很快的構建出應用層的程序,接下來咱們簡單講一下應用層。

五、應用層

應用層位於操做系統用戶態運行,而咱們前面講到的那層是運行在操做系統內核態的:

image-20200726201637207

通常咱們都是經過Socket網絡API來訪問內核態的各層的協議模塊。

常見的應用層協議以下:

  • HTTP:Hypertext Transfer Protocol,超文本傳輸協議,是一個基於請求與響應,無狀態的,應用層的協議,常基於TCP/IP協議傳輸數據,互聯網上應用最爲普遍的一種網絡協議,全部的WWW文件都必須遵照這個標準。設計HTTP的初衷是爲了提供一種發佈和接收HTML頁面的方法;
  • HTTPS:Hypertext Transfer Protocol Secure,安全超文本傳輸協,是HTTP的擴展,用於在計算機網絡上進行安全的通訊,並在Internet上普遍使用;
  • 流媒體:流媒體(streaming media)是指將一連串的媒體數據壓縮後,通過網上分段發送數據,在網上即時傳輸影音以供觀賞的一種技術與過程,此技術使得數據包得以像流水同樣發送;若是不使用此技術,就必須在使用前下載整個媒體文件。
  • P2P協議:Peer-to-peer,計算或聯網是一種分佈式應用程序體系結構,可在對等體之間劃分任務或工做負載;對等方能夠將其部分資源(例如處理能力,磁盤存儲或網絡帶寬)直接提供給其餘網絡參與者,而無需服務器或穩定主機的集中協調。你們常常用的迅雷下載,百度網盤下載就用到了這個協議;
  • WebSocket:WebSocket是一種計算機通訊協議,與HTTP不一樣,WebSocket可經過單個TCP鏈接提供全雙工通訊通道。WebSocket在HTML5規範中最初被稱爲TCPConnection,是基於TCP的套接字API的佔位符。不少在線聊天室,辦公協同軟件都用到了WebSocket。

在應用層,大部分的開發工程師能夠大展拳腳。

  • HTTP協議的實現,最經典的莫過於Tomcat服務器了,關於具體的實現,能夠參考這本書:《深刻剖析Tomcat》;
  • Github上面有一個WebSocket協議的Java實現,感興趣的朋友能夠研究下:Java-WebSocket[5]

到這裏,因爲本文已經兩萬多字了,這裏只作一些知識的延伸,就不進一步展開來說了。

更多關於應用層的相關介紹,我會後續更新,感興趣的朋友記得關注本公衆號進一步跟進學習交流哦


這篇文章的內容就差很少介紹到這裏了,可以閱讀到這裏的朋友真的是頗有耐心,爲你點個贊。

本文爲arthinking基於相關技術資料和官方文檔撰寫而成,確保內容的準確性,若是你發現了有何錯漏之處,煩請高擡貴手幫忙指正,萬分感激。

你們能夠關注個人博客:itzhai.com 獲取更多文章,我將持續更新後端相關技術,涉及JVM、Java基礎、架構設計、網絡編程、數據結構、數據庫、算法、併發編程、分佈式系統等相關內容。

若是您以爲讀完本文有所收穫的話,能夠關注個人帳號,或者點贊吧,碼字不易,您的支持就是我寫做的最大動力,再次感謝!

關注個人公衆號,及時獲取最新的文章。

本文做者: arthinking

博客連接: https://www.itzhai.com/network/comprehend-the-underlying-principles-of-network-programming.html

兩萬字長文50+張趣圖帶你領悟網絡編程的內功心法

版權聲明: BY-NC-SA許可協議:創做不易,如需轉載,請聯繫做者,謝謝!


References

UNIX網絡編程 卷1:套接字聯網API

TCP/IP詳解 卷1:協議(原書第2版). 機械工業出版社

謝希仁. 計算機網絡(第6版). 電子工業出版社

劉超. 趣談網絡協議. 極客時間


  1. 謝希仁. 計算機網絡(第6版). 電子工業出版社. P39 ↩︎

  2. TCP/IP詳解 卷1:協議(原書第2版). 機械工業出版社. P57 ↩︎

  3. TCP/IP詳解 卷1:協議(原書第2版). 機械工業出版社. P116 ↩︎

  4. Homework 6: A raw socket ping tool ↩︎

  5. Java-WebSocket ↩︎

相關文章
相關標籤/搜索