1. 程式人生 > >兩萬字長文50+張趣圖帶你領悟網路程式設計的內功心法

兩萬字長文50+張趣圖帶你領悟網路程式設計的內功心法

# 前言 我大學是學網路工程專業,也就是那種拉網線,面向網線程式設計的。依稀記得學習計算機網路這門課程的時候搭建的`IT宅 itzhai.com`個人網站。 算一下,學這門課程也已經快十年了。 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb64ea688eca40ba832cc6896485414e~tplv-k3u1fbpfcp-zoom-1.image) 某一天,偶然又看到了這本書: ![image-20200726213509847](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIxMzUwOTg0Ny1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 翻了下,發現裡面的內容竟然還是毫不過時,真的是越底層的知識越有價值呀。**我擦了擦書面的灰塵,決定要為它寫點什麼**,於是又從書架上找了相關的書籍: ![image-20200726211636509](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIxMTYzNjUwOS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 來回翻閱和梳理總結,逐漸輸出了這篇文章,獻給對網路不太熟悉,又想快速從入門到熟練的朋友們。 相信大家拿到Socket API,就可以很快寫好程式碼,收發訊息,傳送檔案什麼的,可是底層究竟發生了什麼?TCP、UDP、HTTP是什麼關係、為啥要有WebSocket程式設計。我們從TCP/IP協議棧以及一根網線說起,逐步揭開面向網線程式設計內功心法的面紗。 最後,在這裡解答一個問題:有人問我為什麼要寫公眾號技術文章呢?工作越久,發現身邊比自己年紀小的人越多,我也時常在想,那些同齡人或者比我大的人都去哪裡了,也許有些人忙於家庭生活不亦樂乎,有些人因為公司上市拿到可觀的收入轉行了,也許有人在大公司做起了管理工作,開始走管理路線,帶領團隊創造新的產品。我寫公眾號的原因之一,也就是想告訴大家,我一直在做技術,一個堅持寫程式碼的大齡技術人,並且希望能夠結實更多志同道合的技術人。沒錯,在說你們呢,不要求三連,這篇文章對你感興趣就點個在看唄。Thanks♪(・ω・)ノ 本文的各種電腦、伺服器、路由器小圖示都是我一筆一筆畫的用心只做了儘量美觀有趣好理解的配圖,旨在希望能夠有助於大家理解文章內容,真心希望產品經理也可以看懂。 # 1、網路分層 ## 1.1、OSI七層模型 為了制定一個統一的計算機網路體系,國際標準化組織ISO提出了一個試圖使各種計算機可以在世界範圍內互聯成網的標準框架:OSI/RM(Open System Interconnection Reference Model 開放系統互連基本參考模型),該模型如下: ![image-20200708085822940](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcwODA4NTgyMjk0MC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) **媒介層:**第一到第三層稱為媒體層,它們主要與硬體相關,例如路由,交換和電纜規格; **主機層:**第四到第七層稱為主機層,它們是實現網路服務相關的軟體。 大致介紹一下各層(**注意:看看就好,這不是重點,重點是後面的TCP/IP協議**): * `物理層`:物理層負責在裝置和物理傳輸介質直接傳輸和接收非結構化原始資料。這一層中,把數字位轉換為電,無線電或光訊號。可以發現這一層往往跟各種材質啊訊號呀什麼的打交道,所以稱為物理層; * `資料鏈路層`:在通過物理層連線的兩個節點之間進行傳輸資料幀,檢測並且糾正物理層中可能發生的錯誤。它定義了在兩個物理連線的裝置之間建立和終止連線的協議,還定義了他們之間的流控制協議; * `網路層`:構建和管理多節點網路,包括定址、路由、流量控制。網路層是一種可以連線許多節點的介質,每個節點都在其上有一個地址,通過目標地址就可以在節點之間傳輸資料到目標地址。網路層訊息傳輸不一定要保證可靠,網路層系可以可以提供可靠的訊息傳遞,但不必這樣做; * `傳輸層`:傳輸層提供了將可變長度資料序列從源傳輸到目標主機的功能和過程方法,同時又保持了服務功能的質量。一些協議是面向狀態和麵向連線的,這意味著傳輸層可以進行分段傳輸、支援失敗重傳; * `會話層`:控制計算機之間的連線,負責建立、管理和終止本地和遠端引用程式之間的連線,提供全雙工、半雙工或者單工操作; * `表示層`:網路服務和應用程式之間的資料轉換,如字元編碼、資料壓縮、加密解密; * `應用層`:最接近終端使用者的OSI層,該層直接與實現通訊元件的軟體應用程式進行互動。 可以發現,這個模型還真有點複雜。但很可惜,這個模型似乎不怎麼流行,原因如下: * OSI專家缺乏實際經驗,缺少商業驅動; * OSI協議實現過於複雜,執行效率低; * 標準制定週期長,市場已被其他標準佔據; * 層次劃分不太合理,部分功能在多個分層中出現。 在1980年代末和1990年代初的一段時間內,工程師、組織和國家對哪種標準(OSI模型或[Internet協議套件](https://en.wikipedia.org/wiki/Internet_protocol_suite))將更能塑造最佳和最強大的計算機網路存在爭端,導致兩極分化。儘管OSI在1980年代後期開發了其網路標準,後來更多的供應商網路上更多采用的卻是`TCP / IP`標準,最終`TCP/IP`成為了實時的國際標準。 ## 1.2、TCP/IP四層模型 下面我們把TCP/IP模型和OSI模型放一起對比下: ![image-20200708234353206](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcwODIzNDM1MzIwNi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 可以發現,TCP/IP體系少了表示層和會話層,資料鏈路層和物理層用鏈路層取代。 * `應用層`:最高層,應用層的任務是通過應用程序間互動來實現特定網路應用。主要負責把應用程式中的使用者資料傳達給另一臺主機或同一主機上的其他應用程式。這是所有應用程式協議的執行層,如SMTP、FTP、SSH、HTTP等; * `傳輸層`:負責向兩個主機中的程序之間的通訊提供通用的資料傳輸服務。UDP是基本的傳輸層協議,提供了不可靠的無連線資料報傳輸服務; * `網路層`:負責為分組交換網上的不同主機提供通訊服務。該層定義了定址和路由功能,主要協議是IP協議(Internet Protocol),它定義了IP地址,它在路由中的功能是將資料報傳輸到充當IP路由器的下一個主機,該主機更接近最終資料目的地; * `鏈路層`:也稱為資料鏈路層或者網路介面層,通常包括作業系統中裝置驅動程式和計算機對應的網路介面卡。它們負責處理與傳輸媒介的物理介面細節。 而後面我們除了講到各種協議之外,還會順便提及一些底層硬體,為了更好的進行闡述,我們將把鏈路層再分為物理層和資料鏈路層,採用以下這種五層模型: ![image-20200711112221679](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMTExMjIyMTY3OS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 根據執行模式分為以下兩種: * 運行於使用者程序:應用層,關注應用程式的細節,不關注底層網路通訊細節; * 運行於核心:傳輸層、網路層、鏈路層,在核心中執行,主要處理所有的通訊細節。 介紹了這麼多概念,是不是比較難懂呢,沒關係,我們列一下每一層主要的協議,接下來我們會詳細的講解各種協議的原理。 ## 1.3、分層模型如何工作 這裡我們通過一個FTP客戶端的通訊流程,來說明下兩臺主機是如何工作在TCP/IP分層模型上的: ![image-20200711135012000](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMTEzNTAxMjAwMC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,A主機的FTP客戶端要與B主機的FTP伺服器進行互動。 我們對裝置做一下劃分: * 端系統(End system):A主機和B主機 * 中間系統(Intermediate system):路由器 其中用到的協議也做一下劃分: * 端對端協議(End-to-end):包括應用層和傳輸層,端系統直接直接進行互動; * 逐跳(Hop-by-hop)協議:網路層,需要經過端系統中所有的中間系統。 對於應用層來說,他們好像是直接與端系統進行互動的,應用層根本不知道底層通訊用了多少個路由器,是在乙太網上還是在令牌環網上的。 > **什麼是三層裝置,二層裝置?** > > 如上圖,路由器工作在網路層,屬於第三層,所以經常有人稱他為三層裝置;而後面我們會講到交換機,他是工作在第二層-資料鏈路層,所以也成為二層裝置。 ### 1.3.1、為什麼要分層? 正如上面的例子,分層之後是的頂層遮蔽了底層的物理和通訊細節。底層的通訊原理是在較低的協議層中實現的,因此每個程序都將隱藏大多數通訊細節。以此類推,在傳輸層,通訊表現為主機到主機,而無需瞭解應用程式資料結構和連線的路由器。而在網際網路絡層,則在每個路由器上遍歷各個網路邊界。 就像我們搞軟體開發劃分層次一樣,**分層之後,提高了軟體的複用度,封裝每層細節,使用者只需要關注使用的API就可以了,不用關注實現細節。**你不要告訴我你的一個功能涉及的一萬行程式碼是寫在一個函式裡面的,那太可怕了。 想要了解底層細節的人,就只能拆開TCP/IP協議潘多拉之盒,逐個協議去了解了,這也是本文後邊會繼續探討的內容。高度的封裝,使得頂層開發人員能更快速的通過API開發應用程式。**作為一個API工程師,你知道怎麼呼叫API傳送HTTP請求就夠了,但是作為一個有追求的工程師,你瞭解了這些細節之後,就能夠勝任程式調優以及更加底層的開發工作了。**這也是為什麼我堅持寫[IT宅 itzhai.com](https://www.itzhai.com/)部落格的原因:我想探索技術的本質,而不是生活在API構造的童話世界裡面,這樣即使童話世界謊言被拆穿的那天,也不至於失掉技術的信仰,因為我仍然有能力構建一個新的童話世界。 ![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/386c2426a42747fab16f8a5af6fec1c4~tplv-k3u1fbpfcp-zoom-1.image) ![image-20200711120503245](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMTEyMDUwMzI0NS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 接下來我們看看資料包從在傳輸過程中是如何封裝的。 ### 1.3.2、資料包的封裝和分用 為了演示,我們需要構建一個區域網(LAN)。 假設我們現在直接通過兩個網線把兩臺電腦連起來進行通訊,需要做哪些工作呢。多虧我大學學的是網路工程,也是拉過網線的,所以多少還知道一點: * 準備一根網線,兩個水晶頭; * 水晶頭要做交叉線,採用1-3,2-6交叉接法,保證兩個水晶頭之間能夠正常收發訊號; * 把兩個水晶頭分別插在兩臺主機的電腦上; * 給兩臺電腦配置IP、子網掩碼和閘道器,必須要配置到同一個網路中。 這樣我們就構建好了一個最簡單的區域網了。 ![image-20200726223529480](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIyMzUyOTQ4MC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) #### 1.3.2.1、封裝 所謂封裝,就是每一層都會根據用到的協議,把資料封裝成最終的一個數據單元(不同分層有不同的叫法,參考上圖最左邊每層的描述),每一層拿到的上一層的內容,把上一層封裝好的內容作為當前層的資料,然後加上自己的協議頭或者尾,接著執行該層協議的相關處理邏輯。 有沒有發現,**這有點像裝飾者模式,每一層拿到上一層的內容之後都新增額外的處理邏輯,但是不改變上層傳過來的內容。** ![image-20200726223316970](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIyMzMxNjk3MC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,左邊部分為封裝的過程: * A主機請求B主機的HTTP服務,應用層使用HTTP協議對請求內容進行封裝,加上HTTP請求頭,然後傳給下一層; * 傳輸層拿到HTTP資料,使用TCP協議進行處理,加上自己的`TCP頭`,封裝成`資料段`,通過TCP協議傳輸給下一層。這一層通過TCP協議保證了`可靠的傳輸`; * 網路層拿到TCP傳輸段之後,使用`IP協議`進行處理,加上`IP頭`,封裝成包進一步傳給下一層。這個IP決定了什麼路由或者主機需要接收處理這個包; * 資料鏈路層拿到網路層的包之後,進一步封裝成`資料幀`,最終通過資料鏈路層進行傳送處理,最終資料幀通過`物理層`傳輸給接收端。 #### 1.3.2.2、分用 所謂分用,指的是主機或者中間裝置接收到一個物理層傳輸過來的資料幀時,資料開始從協議棧中由底向上升,逐層處理,每層去掉對應的協議的報文首部。每層協議盒都要檢查報文首部的協議標識進行對應的協議處理。 ![image-20200726223316970](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIyMzMxNjk3MC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 在上圖中,右邊部分為分用的過程: * 主機B接收到物理層傳過來的資料幀之後,首先從首部找到`MAC地址`,判斷是否傳送給自己的,如果不是則進行丟棄; * 如果傳送包是自己的,則從資料幀確定資料`協議型別`,再傳給對應的協議模組,如IP、ARP等; * IP模組接收到資料後獲取`IP首部`,`判斷首部接收的IP`IP地址匹配,如果匹配則根據首部協議型別轉發給對應的模組,如TCP、UDP等; * 傳到TCP模組之後,首先TCP模組會`計算校驗和`,判斷資料的`完整性`,然後處理資料包的`順序接收`相關邏輯;最後檢查`埠號`,確定具體應該要轉發給應用層的哪個應用程式。 * 應用層接收到資料之後,`解析`資料進行`展示`,這裡是HTTP資料包,所以按照HTTP協議的約定進行解析展示。 可以發現,以上流程中,大家感知最深刻的就是傳輸層的TCP或者UDP協議,以及應用層的HTTP協議了,因為做網站開發,或者網路通訊程式設計經常會用到它們的API。 上面介紹的並不是很詳細,不過沒關係,後面我們會把主要的協議都拿出來詳細的講解。 > 當然,**正常的網站請求中,中間肯定會涉及到很多路由器,交換機,光纖等底層的物理裝置,中間會產生很多的逐跳(Hop-by-hop),每個中間系統都會對資料幀進行分用和封裝的過程。** ## 1.4、TCP/IP協議簇 TCP/IP協議簇內容非常多,這裡列出的是本文可能會介紹到的相關協議,以及他們之間的互動關係: ![image-20200726204748249](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcyNjIwNDc0ODI0OS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) # 2、物理層 我們的資料幀究竟是怎麼傳給不同的主機呢。前面我們瞭解到每一個上層都依賴於下層的API,而物理層是最底層的了,它是真的要把資料傳出去了。而資料最終都會變為0和1,物理層依賴於各種不同硬體技術,通過網路的電子傳輸技術,把0和1在傳輸介質中進行傳輸。 ## 2.1、通訊系統的模型 下我我們舉一個最簡單的例子來說明通訊系統的模型[^1]。 很久以前,有些同學家裡都是用的電話線進行上網的,這種網路傳輸模型類似如下這樣: ![image-20200712230429639](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjIzMDQyOTYzOS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,主要包括源系統,傳輸系統,目的系統,可以抽象為下半部分的模型: * 源點:源點產生要傳輸的資料; * 傳送器:源點產生的資料經過傳送器編碼之後進行傳輸; * 傳輸系統:傳輸系統可能是簡單的傳輸線,也可能是複雜的網路系統; * 接收器:接收傳輸系統的訊號,轉換為能夠被目的裝置處理的資訊; * 終點:從接收器獲取傳送過來的數字位元流,最終輸出資訊。 ## 2.2、物理層解決什麼 傳輸媒介的種類非常多:雙絞線、對稱電纜、同軸電纜、光纜、無線通道等,導致物理層的協議種類較多。 物理層的主要作用是遮蔽掉這些傳輸媒介和通訊手段的差異,使物理層上面的資料鏈路層感覺不到這些差異。為此,**物理層需要處理以下事情**: * 規定介面所用接線器的形狀和尺寸,引腳數目和排列,固定和鎖定裝置等; * 規定介面電纜各條線上的電壓範圍; * 規定某一電平電壓的意義; * 規定不同功能的各種可能事件出現順序。 ## 2.3、物理層也出面試題? 最後我列幾個物理層常見的面試題,一般的開發人員都是工作在傳輸層以上,所以考一些TCP,UDP,HTTP,HTTPS等協議我覺得更貼近開發人員真實的工作場景。當然,如果是通訊領域的工程師,物理層都是家常便飯,這些可是通訊的基礎知識。即使知識應用開發工程師,瞭解這些也不會吃虧,說不定哪天親戚還需要叫你幫忙拉網線呢。 下面是幾個常見的物理層面試題: ### 有哪些通訊互動方式?單工、半雙工通訊、全雙工通訊? **單工通訊**,又稱為單向通訊,只有一個方向的通訊,如無線電廣播,電視廣播; ![image-20200712122203747](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjEyMjIwMzc0Ny1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) **半雙工通訊**,又稱為雙向交替通訊,雙方都可以收發資訊,只能交替進行; **全雙工通訊**,又稱為雙向同時通訊,雙方可以同時傳送和接收資料。 ![image-20200712122339778](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjEyMjMzOTc3OC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) ### 為了提高通道利用率,有哪些通道複用技術? 所謂通道複用技術,指的是大家共享一個通道進行通訊,在接收端在使用分用器,把合起來傳輸的資訊分別送到相應的終點; **頻分複用** 使用者在分配到一定的頻帶後,通訊過程中使用都佔用這個頻帶; ![image-20200712123343946](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjEyMzM0Mzk0Ni1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) **時分複用** 將時間劃分為一段段等長時分複用幀,每一個時分複用的使用者週期性的佔用幀位; ![image-20200712123728888](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjEyMzcyODg4OC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) **統計時分複用** 時分複用,如果使用者沒有任何資料要傳輸,也會週期性的給他分配時隙,這就導致了通道利用率不高。 為此出現了統計時分複用。 統計時分複用使用STDM幀來傳送複用的資料,把所有使用者資料按時間順序組成STDM幀,放入一個佇列中,依次傳送出去,這樣就能夠更合理的共享通道。STDM幀中的資料需要新增使用者地址首部資訊,以便能夠正確的分發給目標使用者: ![image-20200712130521935](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjEzMDUyMTkzNS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 這裡的集中器也叫智慧複用器。 除了以上三種,還有波分複用和碼分複用,感興趣的朋友可以自行搜尋資料瞭解,這裡就不繼續展開來講了。 ### 物理層要解決什麼問題? 這個問題上一小節已經回答了。 ## 2.4、物理層裝置之集線器 如果我們只是想用幾臺電腦搭建一個區域網,那麼可以通過集線器(Hub)進行搭建,這個硬體工作在物理層,會把自己收到的位元組都複製到其他埠,如下圖: ![image-20200712162725654](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMjE2MjcyNTY1NC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,其中一臺電腦傳送資訊之後,Hub以廣播的方式發給其他三臺機器,但是究竟哪臺電腦才會把訊息接收下來呢?這裡我們就要講到資料鏈路層了,在這一層判斷資料包是不是自己的。 # 3、資料鏈路層 ## 3.1、資料幀格式 我們首先來看看資料鏈路層的傳輸資料幀的格式。 所有的乙太網(802.3)幀都基於一個共同的格式。在原有規範的基礎上,幀格式已被改進以支援額外功能。 當前乙太網的幀格式[^2]如下: ![image-20200715085955337](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNTA4NTk1NTMzNy1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) `前導`:用在傳送方和接收方之間同步時鐘和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.2、ARP: 如何獲取目標機器的MAC地址? 我們知道,在資料鏈路層,是通過MAC地址判斷某一個接收到的包是不是要進一步處理的。但是如果我們不知道對方的MAC地址的時候,如何傳送資料鏈路層的幀呢?這就需要用到**資料鏈路層的ARP協議**了。 ARP協議:ARP為IP地址到硬體地址之間提供了動態對映,我們通過ARP可以把32位的Internet地址轉換為48位的MAC地址。另外,我們可以使用RARP,把48位的MAC地址轉換為32位的Internet地址。 ![image-20200713220351269](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMzIyMDM1MTI2OS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 另外,為了保證ARP的高效執行,ARP會維護每個主機和路由器上的ARP快取,把Internet地址和MAC地址的對映關係儲存起來,快取正常到期時間是20分鐘。 下面是這個過程的演示,其中ARP資料幀只把關鍵資訊描述出來了,想要了解完整的幀格式可以用參考 `TCP/IP協議詳解卷1`[^3] ![image-20200713231114251](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMzIzMTExNDI1MS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖: 主機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.3.1、為什麼需要交換機? 前面我們用了集線器元件網路,這個時候所有訊息都會廣播到其他埠,可以發現集線器轉發了很多不必要的訊息,能不能只發給需要的埠呢?這個時候就需要用到交換機了。 當一臺電腦A向交換機發送資料時,交換機會把電腦A的IP和MAC地址記住,儲存到一個`轉發表`中,如果`轉發表`中暫時找不到目標IP地址的MAC地址,那麼首先還是會廣播訊息,最終轉發表會記錄所有請求過交換機的電腦IP和MAC。當然,轉發表也是有過期時間的。 ![image-20200713232323689](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxMzIzMjMyMzY4OS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,看到交換機的奸笑沒有,與集線器不同,交換機是有靈魂的的,你告訴他你的身份證號和住址了,他就會偷偷記下來。 ### 3.3.2、為什麼有了IP地址,還需要有MAC地址? IP地址是工作在網路層的,後面會講到; MAC地址是工作在資料鏈路層的,也就是交換機這一層,交換機之間的主機進行通訊,都是用的MAC地址,但是一旦走出了局域網,我們就得用大家都公認的IP地址了。 MAC地址就好像是我們的身份證,IP就像是我們的住址,可以根據住址寄送快遞,但是不能根據身份證號碼寄快遞,別人不知道怎麼走呢。 一個區域網,用身份證沒有問題呀,因為要找某個人,ARP會大喊一聲名字,那個人就會告訴你他的身份證號碼了,這個時候直接以身份證作為標識傳訊息,別人聽到不是自己的身份證就不管了。最終交換機這個小管家記住了所有人的名字跟身份證號碼,就會使用悄悄話的方式傳達訊息了。這也就是用到了交換機的轉發表。 ### 3.3.3、交換機拓撲環路問題 假設我現在拉網線,搞了一個這樣的拓撲結構: ![image-20200714225908316](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNDIyNTkwODMxNi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 如上圖,主機準備傳送一個訊息出去,結果交換機B收到後,複製資料幀,傳送給了交換機A、C、D,此時交換機B認為主機是在左邊。但是不妙的事情發生了,交換機D收到訊息後,由於轉發表還是空的,又是也複製資料幀,轉發到了交換機A、B、C,這個時候交換機B發現怎麼主機的資料又從右邊傳過來了,這些徹底暈了,不知道主機究竟在哪裡。就這樣資料一致在這個網路裡面打轉,這就拓撲環路導致的問題。 #### 生成樹協議 為了解決以上問題,於是 有了生成樹協議(Spanning Tree Protocol,STP)。 STP通過在每個交換機禁用某些埠工作,來避免拓撲環路,保證不會出現重複路徑。 STP會找到拓撲結構的一個生成樹,通過生成樹避免環路。生成樹的形成和維護有多個網橋完成,在每個網橋上執行一個分散式演算法。 以上拓撲,結構,最終應用了生成樹,禁用一些埠之後,可能會是這樣: ![image-20200714225935282](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNDIyNTkzNTI4Mi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 這樣,訊息就不可能再傳回左邊的主機了,從而避免了拓撲環路導致的問題。 **建立生成樹:** 網橋會發送一種稱為網橋協議資料單元(BPDU)的幀來輔助形成和維護生成樹。 STP首先會嘗試選舉根網橋,根網橋是在網路中識別符號最小的網橋(也就是說優先順序與MAC地址結合),網橋初始化的時候,假設自己是最小的網橋,然後用自己的網橋ID作為根ID欄位的值傳送配置BPDU訊息,如果發現ID更小的網橋,那麼會停止傳送自己的幀,並基於接收到的ID更小的幀構造下一步傳送的BPDU訊息。發出根ID更小的BPDU埠被標記為根埠,剩餘埠被設定為阻塞或者轉發狀態。 ## 3.4、VLAN # 3、網路層 網路層,Internet layer,最熟知的就是IP協議了(Internet Protocol)。 前面我們將的資料鏈路層,其實只能在區域網內進行通訊,因為都是通過MAC地址進行傳達資訊的,要想跨區域網,那麼就得用到IP地址了,這就是網路層要做的事情了。 首先我們來介紹下網路的一個協議:ICMP協議。 ## 3.1、ICMP協議 IP協議本身不支援發現發往目的地地址失敗的IP資料包,也沒有提供直接的方式獲取診斷資訊,比如在傳送途中,經過了哪些路由器,以及往返時間。 為此,就有了`ICMP協議`(`Internet Control Message Protocol`,`ICMP`)專門來負責這些事情。 **ICMP並不為IP網路提供可靠性,它只是用於反饋各種故障和配置資訊。丟包不會觸發ICMP。** > ICMP是[RFC 792中](https://tools.ietf.org/html/rfc792)定義的Internet協議套件的一部分。ICMP訊息通常用於診斷網路或探測網路目的,或者是為了響應[IP](https://en.wikipedia.org/wiki/Internet_Protocol)操作中的錯誤而生成(如[RFC 1122中](https://tools.ietf.org/html/rfc1122)所指定),ICMP錯誤響應給原始資料包的源IP地址。 但是黑客經常用ICMP來做壞事,於是網路管理員可能會用防火牆阻止掉ICMP報文,這樣的話,很多ping、traceroute之類的診斷程式就無法正常工作了。 ### 3.1.1、格式 ICMP報文是在IP資料報內部傳輸的,格式如下: ![image-20200715084813447](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNTA4NDgxMzQ0Ny1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 而ICMP報文的格式如下: ![image-20200715085522615](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNTA4NTUyMjYxNS1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 其中: * 型別有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.2、查詢報文 查詢報文是有關資訊採集和配置的ICMP報文。 **我們經常用到的ping程式就用到了ICMP查詢報文。** #### ping程式 ping程式會發送一份ICMP回顯請求給主機,並等待返回ICMP回顯應答。 **ping程式ping不通了,就不能訪問對應的主機了嗎?** 我們知道,網路管理員可能會用防火牆阻止掉ICMP報文的,這樣我們可能就ping不通了,但是主機的可達性不能只取決於IP層是否可達,還與埠號和協議有關,而ping是執行在網路層的,用於測試網路連線狀態和資訊包傳送接收狀況,即使ping不通,我們也可能用telnet遠端登入到主機的其他埠,如25號埠。 ping程式用到了回顯請求和回顯應答報文,報文格式如下: ![image-20200715233138672](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNTIzMzEzODY3Mi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) Unix系統實現ping程式時,把ICMP報文的識別符號設定為程序ID,在程序內,序號從0開始,每傳送一次新的 回顯請求就加1,這樣就可以同時執行多個ping程序了。 **ping程式的埠號是什麼?** 埠號是傳輸層的東西,ping程式是使用ICMP協議,直接跳過了傳輸層,所以呢,ping程式是沒有所謂的埠號的。 我們傳送一個ping請求,資料在協議棧中的處理流程如下: ![image-20200716085927626](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNjA4NTkyNzYyNi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 1. A主機的ping應用程式向伺服器發起回顯請求,說了一句:**hi** 2. 直接傳輸到網路層的ICMP協議,進行ICMP資料封裝: 1. ![image-20200716085942614](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNjA4NTk0MjYxNC1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 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](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uaXR6aGFpLmNvbS9pbWFnZS0yMDIwMDcxNjA4NTk1MzYxNi1hLnBuZy1pdHpoYWk?x-oss-process=image/format,png) 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](https://cs450.class.uic.edu/assignments/homework-6-a-raw-socket-ping-tool/)[^4],思路都在這裡了,大家動手做一做,然後就可以有直接操作網路層的工作經驗了。