什么是 ICMP 協議
關于這點我們在 IP 協議那篇文章中提過一嘴,IP 協議作為一種提供不可靠資料交付的網路層協議,在傳輸的程序中,其 IP 資料報可能會發生丟失、重復、延遲和亂序等各種情況, 但是 IP 協議對這些糟糕的情況并不擁有有效的檢測和彌補措施,當然更不會將這些結果通知收發雙方,
為此,鑒于上述原因,我們在構建 IP 網路時,就需要特別注意兩點:
- 確認網路是否能夠正常作業
- 即使診斷出現例外時的原因所在
于是,網際控制報文協議(Internet Control Message Protocol,ICMP)出現了,
形象來說,IP 協議就好像一個將軍,而 ICMP 協議就是他手下的情報員,將軍運籌帷幄于千里之外,而在前線浴血奮戰的士卒們傷亡當然也在所難免,
那無法親臨前線的將軍最起碼要知道兩件事情:第一點,我的士卒們是不是按照我指引的方向在前進著,別一陣猛沖發現沖錯了地方;第二點,我的士卒們傷亡多少,被什么所傷,了解了己方傷亡的原因才好做下一步的戰略部署,總不能死了個不明白,不必多說,這就是情報員 ICMP 該做的事情了,
當然了,上述只是打個比方,可能不是很嚴謹,各位知道什么意思就行,不必過于吹毛求疵,
這里我們再用學術點的語言來總結下,ICMP 的主要功能有如下兩點:
1)確認 IP 資料報是否成功送達目標地址
2)如果某個 IP 資料報因為某種原因未能正常到達目的地,則由 ICMP 負責通知具體的原因
ICMP 報文初探
具體的出錯原因是 ICMP 協議負責通知的,這個通知的學名就是 ICMP 報文,那么 ICMP 報文是由發送方發送方發出的還是由接收方發出的呢?
都不是,
ICMP 報文是由路由器發出來的,
舉個例子:主機 A 在不知情的情況下向主機 B 發送了資料包,而主機 B 正在呼呼大睡,主機 A 和主機 B 不在同一個區域網內,假設它倆之間會經過兩個路由器,看下圖:

眾所周知,除了 IP 地址我們還需要 MAC 地址才能確保資料包精準的找到傳送方向,因此,路由器 2 為了知道主機 B 的 MAC 地址,它會廣播一個 ARP 請求報文,希望獲取到主機 B 的 MAC 地址,而主機 B 都關機了自然也就無法應答這個請求報文了,
為此,路由器 2 會一遍又一遍的重新發送著 ARP 請求報文,在多次無果后,路由器 2 就會回傳一個 ICMP Destination Unreachable 的包給主機 A(關于 ICMP 報文型別下文會講),通知主機 A,非常遺憾,您發往主機 B 的包未能成功抵達,
那么,ICMP 報文具體是怎么傳輸給主機 A 的呢?
這個很簡單,TCP/UDP 報文是怎么傳輸的,ICMP 報文就怎么傳輸,
也就是說,真正的資料首先會被加上 ICMP 首部,封裝成 ICMP 報文,然后被 IP 協議封裝成 IP 資料報進行明文傳輸,由 IP 協議指定源 IP 地址和目的地址,主機 A 收到資料后會一層一層解封裝,從而獲得真正的資料得知發生例外的原因,遂大怒一聲:蠢貨主機 B,
ICMP 報文格式
至此,各位已經知道了,ICMP 報文是被封裝在 IP 資料報里面的,我們來看看下圖:

額,這里好像沒啥好說的,上圖畫的很 Nice ,是我之前考研的時候看 B 站上的王道視頻截下來的,各位看明白上圖,了解 ICMP 報頭有哪些東西,知道型別和代碼這兩個欄位很重要就好了,尤其是型別這個,接下來我們先重點講它,
ICMP 報文型別
上文提到了 ICMP Destination Unreachable,也就是目標不可達的 ICMP 報文,
ICMP 報文型別大體上可以分為兩種,差錯報文和詢問報文,解釋一下:
所謂查詢報文就是,用于主機進行診斷的查詢訊息,
這么學術性的文字可能不是很好理解,這樣,咱形象來說,查詢報文其實和通信例外沒啥關系,查詢報文就好比將軍率領著千軍萬馬來到了一片寂靜的峽谷,正是一個容易被埋伏的地方,將軍不敢貿然前進,于是派遣幾個情報員前去探明敵情,一有動靜立馬回報,
常見的 ICMP 查詢報文型別有以下幾種:
- 回送應答(Echo Reply),對應 ICMP 報文首部型別欄位的值:0
- 回送請求(Echo Request),對應 ICMP 報文首部型別欄位的值:8
而差錯報文就是,用于通知主機出錯的原因,顯然,ICMP 差錯報告報文是伴隨著出錯資料產生的,一旦 IP 協議發現某個 IP 資料報出錯了,首先就會毅然地丟棄出錯的這個 IP 資料報,然后發送 ICMP 差錯報文,
常見的 ICMP 差錯報文型別有以下幾種:
- 目標不可達(Destination Unreachable),對應 ICMP 報文首部型別欄位的值:3
- 原點抑制(Source Quench),對應 ICMP 報文首部型別欄位的值:4
- 重定向或改變路由(Redirect),對應 ICMP 報文首部型別欄位的值:5
- 超時(Time Exceeded),對應 ICMP 報文首部型別欄位的值:11
下面詳細解釋一下這幾個常見的 ICMP 報文型別,
ICMP 回送訊息(型別 0、8)
用于進行通信的主機或路由器之間,判斷所發送的資料包是否已經成功到達對端的一種訊息,
可以向對端主機發送 ICMP 回送請求的訊息(Echo Request,型別 8),也可以接收對端主機發回來的 ICMP 回送應答訊息(Echo Reply,型別 0)

我們常用的 ping 命令就是基于 ICMP 回送訊息實作的,
ping 這個單詞源自聲納定位,而這個命令的作用也確實如此,它發送型別為 0 的 ICMP Echo Request 訊息,收到請求的主機則用型別為 8 的 ICMP Echo Reply 訊息進行回應,ping 就會計算發送 Requenst 和接收到 Reply 的訊息間隔時間,并計算有多少個包被送達,丟失了多少個包等,用戶就可以據此判斷網路大致的情況,
如下圖我們來 ping 一下 Github:

ping 也并不是啥事也沒做,它在 ICMP 報文格式中又添加了兩個欄位:識別符號和序號,這倆其實很好理解:
1)識別符號用來區分是哪個應用程式發 ICMP 包,
形象來說,將軍派出了兩個情報員,一個用來是了解戰況的,一個是用來搬救兵的,那總得有個標識區分這倆情報員吧,識別符號就是干這事的,最容易想到的能作為識別符號的東西,想來也不用我多嘴吧,就是行程的 PID,
2)序號用來確認網路包是否有丟失,
形象來說,將軍派出了 10 個情報員,給每個情報員都編個號,這樣,如果派出去 10 個,回來 10 個,就說明前方戰況不錯;如果派出去 10 個,一個也沒回來或者就回來 1 個,說明情況不妙啊,
ICMP 目標不可達訊息(型別 3)
路由器無法將 IP 資料報發送給目標地址時,會給發送端主機回傳一個目標不可達(Destination Unreachable Message)的 ICMP 訊息,
那目標不可達有多種可能的原因,比如說網路問題、目標主機問題等等,所以這個目標不可達訊息還需要指明不可達的具體原因,這個具體原因就記錄在 ICMP 報頭的代碼欄位,
那么這里我們仍然以行軍打戰的例子來看看常見的目標不可達型別的代碼有哪些:
1)前方戰事吃緊,將軍(主機 A)派了一隊士兵回京城找皇上(主機 B)搬救兵,中途情報員快馬加鞭趕到匯報:將軍,我們在途中迷失了方向,找不到京城在哪,這就是網路不可達,其代碼為 0
2)假設士兵們成功回到京城,但是皇上出城了,不在京城,朝廷百官也不敢私自同意出兵救援,這就是主機不可達,到了地方卻沒找到人,其代碼為 1
3)假設士兵成功找到了京城,但是由于將士們常年在外征戰,守城的年輕護衛們已經不認識這些威名赫赫的將士們了,所以需要進城口令證明身份,但是久經沙場的將士們一時半會想不起來這些東西,遂無法進城,這就是協議不可達,其代碼為 2
4)假設士兵們成功進了城,也成功面見了圣上,但是皇上卻說密偵司告訴他的情報和你們說的不一樣,你們說你們需要的是救兵,而我得到的訊息是你們只需要糧草,這就是埠不可達,其代碼為 3
5)假設士兵們成功求得了救兵,并且獲得了火器十余箱,但是中途山路狹窄,裝火器的馬車太大過不去,為此需要換小一點的馬車,每個馬車裝一點,但是由于火器技術尚不成熟,考慮安全問題,將軍早就嚴令禁止把火器分裝,于是乎,浩浩蕩蕩的援兵阻塞在了狹窄的山路,這就是需要進行分片但設定了不分片位,其代碼為 4
ICMP 重定向訊息(型別 5)
說到這個,我們先要明白 IP 協議或者網路層的職責是什么,就是選擇合適的網間路由和交換結點, 確保資料的及時傳送,
為此,我們總是傾向于基于最短最優的路徑進行傳輸,
那么如果路由器發現發送端主機使用了某個不是最優的路徑發送資料,他就會回傳一個 ICMP 重定向訊息(ICMP Redirect Message)給這個主機,并且,在這個訊息中包含了最優的路由資訊和源資料,
寫上癮了哈哈,舉個例子:將軍得知 ICMP 的情報后震怒,派出去搬救兵的領隊竟然帶著十萬救兵在繞彎子,亂臣賊子,將軍趕緊下令誅殺這個領隊并立即走最近的路趕回來,
ICMP 超時訊息(型別 11)
IP 包中有一個欄位叫做 TTL (Time To Live,生存周期),它的值隨著每經過一次路由器就會減 1,直到減到 0 時該 IP 包會被丟棄,
此時,IP 路由器將會發送一個 ICMP 超時訊息(ICMP Time Exceeded Message)給發送端主機,并通知該包已被丟棄,
形象來說,就是將軍派出去的搬救兵的那隊人馬苦于找不到京城的方向,路途開始帶上的只夠三天的糧草斷盡,全隊飲恨而死,
設定 IP 包生存周期的主要目的,是為了在路由控制遇到問題發生回圈狀況時,避免 IP 包無休止地在網路上被轉發,
ICMP 的應用
其中一個應用 ping 命令我們已經說過了,它是基于 ICMP 查詢報文的,
還有一個命令,是基于 ICMP 差錯報文的,在 Linux 下這條命令是 traceroute,在 Windows 是 tracert,
大家可能會覺得 ICMP 差錯報文是只有在通信例外的時候才會生成,其實不然,traceroute 命令就是一個例外,它會使用 ICMP 的規則,故意制造一些能夠產生例外的場景,

traceroute 命令有兩大作用:
1)故意設定特殊的 TTL,來追蹤去往目的主機上沿途經過的路由器,
具體來說,就是發送端主機會不斷的向接收端主機發送 UDP 報文,UDP 報文被封裝成 IP 資料報,同時將 TTL 從 1 開始按照順序遞增,
比如說,將 TTL 設定 為 1,那么遇到第一個路由器的時候,這個 IP 資料報就會被丟棄,接著回傳 ICMP 差錯報文,型別是 ICMP 超時訊息,
接下來將 TTL 設定為 2,第一個路由器過了,遇到第二個路由器時這個 IP 資料報就會被丟棄,接著回傳ICMP 差錯報文,
......
這樣,traceroute 就拿到了所有路由器 IP,
那到這里其實還有一個問題,怎么知道資料到底有沒有到達目的主機呢?
traceroute 是基于 UDP 傳輸的,那自然是需要指定一個埠號的,traceroute 會選擇一個不可能的值作為 UDP 的埠號,
這樣,當資料到達目的主機時,就會發現埠對不上,于是路由器會產生一份 ICMP 目標不可達訊息,其代碼是 3,即埠不可達,
當發送端主機接收這份埠不可達的 ICMP 報文時,就知道目的主機成功收到了資料,
2)故意設定不分片,從而確定路徑的最大傳輸單元 MTU,
某些情況下我們并不知道路徑的 MTU 大小,所以我們需要某種手段去獲取 MTU,才能控制發送的資料包的大小,
發送端主機要做的作業很簡單,就是像往常一樣發送 IP 資料報,但是將 IP 首部的分片禁止標志位置為 1,
這樣,如果 IP 資料報的長度超過了 MTU,該資料報會被路由器直接丟棄,并且給發送端主機發送 ICMP 目標不可達訊息,其代碼為 4,即需要進行分片但設定了不分片位
這樣,發送端主機每次收到 ICMP 需要進行分片但設定了不分片位訊息時就減小 IP 資料報的長度,直到順利到達目標主機,
?? 關注公眾號 | 飛天小牛肉,即時獲取更新
- 博主東南大學碩士在讀,攜程 Java 后臺開發暑期實習生,利用課余時間運營一個公眾號『 飛天小牛肉 』,2020/12/29 日開通,專注分享計算機基礎(資料結構 + 演算法 + 計算機網路 + 資料庫 + 作業系統 + Linux)、Java 技術堆疊等相關原創技術好文,本公眾號的目的就是讓大家可以快速掌握重點知識,有的放矢,關注公眾號第一時間獲取文章更新,成長的路上我們一起進步
- 并推薦個人維護的開源教程類專案: CS-Wiki(Gitee 推薦專案,現已累計 1.6k+ star), 致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習 ~ ??
- 如果各位小伙伴春招秋招沒有拿得出手的專案的話,可以參考我寫的一個專案「開源社區系統 Echo」Gitee 官方推薦專案,目前已累計 700+ star,基于 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ... 并提供詳細的開發檔案和配套教程,公眾號后臺回復 Echo 可以獲取配套教程,目前尚在更新中,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/281125.html
標籤:其他
下一篇:Python串列介紹
