程式設計師應該瞭解的網路協議

來源:文萃谷 8.4K

引導語:網路協議是進行網路連通訊的重點,以下是小編整理的程式設計師應該瞭解的網路協議,歡迎參考閱讀!

程式設計師應該瞭解的網路協議

  網路路由

在過去的模擬電話中,撥打電話意味著從電話到朋友那裡具有真實的電線連線。就好像電線直接從你那裡連線到他們那裡。雖然沒有這樣實際的電線,當然——連線是通過複雜的開關係統進行的——但它與電線相當。

有太多的Internet節點可以這樣工作。但是我們不能從每個機器到要和其對話的其他機器提供直接、不間斷的路徑連線。

相反,資料是桶式的,從一個路由器傳遞到下一個路由器,在鏈路中,每個路由器使資料更靠近目的地。 我的膝上型電腦和谷歌伺服器之間的每個路由器連線到其他多個路由器,這樣維護著一個粗略的路由表,顯示哪些路由器更接近網際網路的哪個部分。當一個數據包到達的目的地是谷歌伺服器時,路由表中的快速查詢功能告訴資料包走哪個路由器能夠更加靠近谷歌伺服器。資料包很小,所以鏈路中的每個路由器只會對下一個路由器佔用一小段時間。

路由解決的問題可分為兩個子問題。首先,解決:資料的目的地是什麼?這由IP、網際網路協議、IP地址處理。IPv4仍然是IP的最常見版本,僅提供32位地址空間。現在已完全分配,因此將一個節點新增到公共Internet需要重用現有的IP地址。 IPv6允許2128個地址(約1038個),但截至2017年只有約20%被使用。

現在我們有地址,我們需要知道如何通過網際網路將資料包路由到其目的地。路由過程發生得很快,所以沒有時間查詢遠端資料庫的路由資訊。例如,Cisco ASR 9922路由器的最大容量為每秒160兆位元。假設完整的1500位元組資料包(12,000位),也即是在一個19英寸機架中每秒13333333333個數據包!

為了快速路由,路由器維護路由表,指示各種IP地址組的路徑。當新資料包到達時,路由器在表中查詢它,告訴它哪個對等體最接近目的地。它將資料包傳送給該對等體並移動到下一個。BGP的工作是在不同路由器之間傳遞此路由表資訊,確保是最新的路由表。

不幸的是,IP和BGP一起並不能搭建有效的網路,因為它們沒有辦法可靠地傳輸資料。如果路由器過載並丟包,我們需要一種方法來檢測丟失並請求重傳。

  分組交換

如果網際網路通過路由器將網路中彼此之間的資料相互連線起來,那麼當資料量大時會發生什麼? 如果我們請求88.5 MB的JavaScript Birth & Death視訊,應該怎麼辦?

我們可以嘗試設計一個網路,其中88.5 MB文件從Web伺服器傳送到第一個路由器,然後到第二個,以此類推。不幸的是,該網路在網際網路規模上甚至內部網路規模都無法工作。

首先,計算機是具有有限儲存量的有限個機器。如果一個給定的路由器只有88.4 MB的緩衝區可用,它根本不能儲存88.5 MB的視訊檔案。因此資料將被丟棄,更糟糕的是,這沒有任何跡象。如果路由器十分繁忙而丟失資料,那麼無法花時間告訴我丟棄的資料。

二是電腦不可靠。有時,路由節點失敗。 有時,船舶的錨點會意外損壞關乎大量網際網路資料的水下光纖電纜。

由於這些原因,我們不會通過網際網路傳送88.5 MB的郵件。相反,我們將它們分解成資料包,通常在每個1,400位元組左右。我們的視訊檔案將分為63,214個單獨的資料包進行傳輸。

  無序資料包

通過資料包捕獲工具Wireshark測量JavaScript的Birth & Death實際傳輸情況,我收到的總共接收到61,807個數據包,每個是1,432個位元組。將這兩個相乘,我們得到88.5兆位元組資料,這是視訊的大小。(這不包括各種協議增加的開銷,如果包括,我們會看到稍高一些的資料大小)

傳輸是通過HTTP完成的,一種通過TCP分層的協議,即傳輸控制協議。它只需要14秒,因此資料包的平均速率約為每秒4,400,或每包約250微秒。在14秒鐘內,我的機器收到全部的61,807個數據包,可能是無序的,當它們進來時將它們重新組裝成完整的檔案。

使用最簡單的機制重新組裝TCP包,可以想象:一個計數器。每個資料包傳送時都會分配一個序列號。在接收端,按照序列號對資料包進行排序。一旦這一切都順利,沒有差別,我們知道檔案是完整的。(實際的TCP序列號往往不是整數,每次只增加1,但這個細節在這裡並不重要。)

我們怎麼知道檔案何時完成?TCP並不能回答,因為這是更高階協議的工作。例如,HTTP會有包含一個“Content-Length”頭,指定響應長度(以位元組為單位)。客戶端讀取Content-Length,然後繼續讀取TCP資料包,將其組裝成原始順序,直到它具有Content-Length指定的長度。這就是HTTP頭(和大多數其他協議頭)先於響應有效負載到來的一個原因:否則,我們不知道有效負載的大小。

當我們在這裡說“客戶”時,我們實際上在談論整個電腦接收端。TCP重組發生在核心內部,因此Web瀏覽器和curl和wget等應用程式不必手動重新組合TCP資料包。但核心不處理HTTP,因此應用程式必須瞭解Content-Length標頭檔案,並知道要讀取的位元組數。

使用序列號和分組重新排序,即使資料包無序到達,我們也可以傳送大的位元組序列。但是如果一個數據包在傳輸中丟失了,會在HTTP響應中留下一個空白嗎?

  傳輸視窗和慢啟動

我們一邊開著Wireshark,一邊下載“Death and Death of JavaScript”。通過瀏覽捕獲的結果,我們看到源源不斷的資料包被接收到。

例如,序列號為563,321的資料包到達。像所有TCP資料包一樣,它有一個“下一個序列號”,它是用於下一個資料包的數字。該資料包的“下一個序列號”為564,753。事實上,下一個資料包的序列號也的確為564,753,所以一切都很好。當連線達到一定速度後,每秒鐘會發生數千次傳輸。

有時候,我的電腦會向伺服器傳送一條訊息,例如“我已收到所有資料包,包括資料包564,753”。這是一個ACK,用於確認:我的電腦確認收到伺服器的資料包。在一個新的連線上,Linux核心每十個資料包傳送一個ACK。這由TCP_INIT_CWND常量控制,我們可以在Linux核心的原始碼中定義它們。(TCP_INIT_CWND中的CWND表示擁塞視窗:一次允許的資料量)如果網路擁塞(超載),視窗大小將減少,傳輸資料包變慢)。

十個資料包大約是14 KB,所以我們一次限制在14 KB資料。這是TCP慢啟動的一部分:連線從小擁塞視窗開始。如果沒有資料包丟失,接收機將不斷增加擁塞視窗,允許一次傳輸更多的資料包。

有時,資料包將發生丟失,所以接收視窗會縮小,傳輸速度變慢。通過自動調整擁塞視窗以及一些其他引數,傳送方和接收方保持資料移動速度在網路允許的情況下儘可能快,但不會超出限制。

這種情況發生在連線的兩端:每一方都會確認對方的訊息,並且每一方都保留自己的擁塞視窗。非對稱視窗允許協議充分利用具有不對稱上行和下行頻寬的網路連線,比如大多數住宅和移動網際網路連線。

  可靠傳輸

電腦不可靠:由計算機組成的網路是不可靠的。在像因特網這樣的大型網路中,故障是執行的正常部分,必須適應。在分組網路中,這意味著重傳:如果客戶端接收到資料包1和3,但沒有接收到2,則需要請求伺服器重新發送丟失的資料包。

當每秒接收數千個數據包時,如我們的88.5 MB視訊下載,錯誤幾乎是必然的。為了證明,讓我們回到我的Wireshark捕獲的下載。對於數千個數據包,一切正常。每個資料包指定一個“下一個序列號”,後面是另一個包含該數字的資料包。

突然間出了問題。第6,269個數據包具有7,208,745的“下一個序列號”,但該資料包一直沒有到來。相反,序列號為7,211,609的資料包到達。這是一個亂序的資料包:意味著缺少一些東西。

我們不清楚這裡出了什麼問題,也許網際網路上的一箇中間路由器過載,也許我的本地路由器超載,也許有人使用微波爐,引入電磁干擾並減慢了我的無線連線。在任何情況下,資料包丟失,唯一的指示是接收到意外的資料包。

TCP沒有特殊的“我丟了一個包!”資訊。相反,ACK被巧妙地用於指示丟失。當出現亂序的資料包時,接收機會re-ACK最後一個好的資料包——即正確順序的最後一個數據包。實際上,接收機正在說“我收到了我正在確認的資料包5,之後也收到了一些資訊,但是我知道它不是資料包6,因為它與資料包5中的下一個序列號不匹配。

如果兩個資料包在傳輸過程中被交換了順序,則會導致單個額外的ACK,並且在收到無序資料包後,所有內容都將繼續正常傳輸。但是如果資料包真的丟失,意外的資料包將繼續到達,並且接收器將繼續傳送最後一個好資料包的ACK副本。這可能導致數百個重複的ACK。

當傳送方在一行中看到三個重複的ACK時,它假定下一個資料包丟失並重發。這被稱為TCP快速重傳,因為它比以前基於超時的方法更快。有趣的是,協議本身沒有任何明確的方式說“請立即重發!”相反,協議產生的多個ACK自然用作了觸發器。(一個有趣的思考實驗:如果一些重複的ACK丟失,從未到達傳送者,會發生什麼?)

即使網路正常工作,重傳也是很常見的。在我們的88.5 MB視訊下載中,我看到了這一點:

由於持續成功的傳輸,擁塞視窗迅速增加到大約一兆位元組;

1)有幾千個包順序出現,一切正常;

2)一個數據包發生故障;

3)資料繼續以兆位元組/秒計算,但資料包仍然丟失;

4)我的機器傳送最後一個已知好資料包的數十個重複的ACK,但是核心還儲存待處理的無序資料包以供稍後重新組裝;

5)伺服器接收重複的ACK並重新發送丟失的資料包;

6)我的客戶端對以前丟失的資料包和隨後由於無序傳輸已經收到的資料包進行ACK。這是通過簡單地確認最近的資料包來完成的,這個資料包也隱含地對所有較早的資料進行ACK。

7)傳輸繼續,但是由於丟失的資料包,擁塞視窗縮小。

這個是正常的,這是發生在我所完成的完整下載的每個捕獲中。儘管我們認為日常生活中網路是不可靠的,甚至TCP在正常條件下也會偶爾失敗,但是TCP仍然是工作得很出色的。

  物理網路

所有這些網路資料必須通過物理介質(如銅纜,光纖和無線電)傳輸。在物理層協議中,乙太網是最為人所知的。在網際網路的早期,它的受歡迎程度使我們設計其他協議以適應其限制。

首先,讓我們認識物理細節。乙太網與RJ45聯結器關係最為緊密,RJ45聯結器看起來像更大的八針版本的老式四針手機插孔。它還與cat5(或cat5e或cat6或cat7)電纜相關聯,其中包含絞合成四對的`八根匯流排。這裡還存在其他媒體,如我們最有可能在家中遇到的:八根電線纏繞在與8針插孔相連的護套中。

乙太網是物理層協議:它描述了這些位如何變成電纜中的電訊號。它也是一個鏈路層協議:它描述了一個節點與另一個節點的直接連線。但是,它純粹是點對點的,並且與資料在網路上的路由無關。在這一層沒有TCP概念中的連結概念,也沒有IP地址意義上的可重新分配地址的概念。

作為協議,乙太網有兩個主要的工作。首先,每個裝置需要注意它連線的裝置,需要協商一些連線速度等引數。

第二,一旦鏈路建立,乙太網需要攜帶資料。像較高階協議如TCP和IP一樣,乙太網資料被分解成資料包。資料包的核心是一個幀,它有一個1500位元組的有效載荷,再加上另外22個位元組的頭資訊,如源和目標MAC地址,有效載荷長度和校驗和。經常處理地址長度與校驗和的程式設計師對這些領域是很熟悉的,我們可以想象為什麼它們是必要的。

然後將幀包含在另一層的header中以形成完整資料包,但這些header很奇怪。他們開始與模擬電氣系統的基礎現實相反,所以他們看起來似乎不會被放在軟體協議中。完整的乙太網資料包包含:

1)前導碼是56位(7位元組)的交替1和0。裝置使用它來同步他們的時鐘,就像當人們計數“1-2-3-GO!”時一樣。計算機無法計數超過1,所以他們通過說“101010101010101010101010101010101010101010101010101010”進行同步。

2)8位(1位元組)起始幀分隔符,為171(10101011為二進位制),這標誌著前導碼的結束。請注意,再次重複“10”,直到“11”為止。

3)如上所述,幀本身包含源和目的地址,有效載荷等。

4)96位(12位元組)的間隔間隙,其中line處於空閒狀態。大概這是讓裝置休息,因為它們很累。

把這一切放在一起:我們想要的是傳輸我們的1500位元組的資料。我們新增22個位元組來建立一個幀,它指示源,目標,大小和校驗和。我們再新增20個位元組的額外資料來滿足硬體需求,建立完整的乙太網資料包。

你可能認為這就是堆疊的底層。並不是這樣,反而因為模擬世界變得越來越複雜,堆疊底層也變得越來越複雜。

  當網路遇見現實世界

數字系統不是真實存在的;所有一切都是模擬的。

假設我們有一個5伏CMOS系統。(CMOS是一種數字系統,如果你不熟悉也不用擔心)這意味著一個完全開啟的訊號將是5伏,一個完全關閉的訊號將是0。但沒有什麼是永遠完全關閉或完全關閉的,因為物理世界不允許。實際上,我們的5伏CMOS系統將會考慮將高於1.67伏的任何值記為1,而低於1.67的任何值都將為0。(1.67是5的1/3,我們不用擔心為什麼閾值是1/3,如果你想深入研究,維基百科有一篇相關文章!而且,乙太網不是CMOS甚至不與CMOS相關,CMOS和其1/3的值僅用於簡單的說明。)

我們的乙太網資料包必須通過物理線路,這意味著更改電線上的電壓。乙太網是一個5伏系統,所以我們期望乙太網協議中的每1位為5伏,每個0位為0伏。但是有兩個竅門:首先,電壓範圍是-2.5 V到+2.5 V。其次,更奇妙的是,每組8位在到達電線之前就會擴充套件成10位。

有256個可能的8位值和1024個可能的10位值,因此可以將其對映到表中。每個8位位元組可以對映到四個不同的10位模式中的任一個,每個模式將被轉回到接收端的同一個8位位元組。例如,10位值00.0000.0000可能對映到8位值0000.0000。但也可能10位的值10.1010.1010也對映到0000.0000。當乙太網裝置看到00.0000.0000或10.1010.1010時,它們將被理解為位元組0(二進位制0000.0000)。(警告:現在有一些電子相關的術語。)

這是為了提供特別的類比電路需求:平衡裝置中的電壓。假設這個8位到10位編碼不存在,我們傳送一些恰好全是1的資料。乙太網的電壓範圍為-2.5至+2.5伏,因此我們將乙太網電纜的電壓保持在+2.5 V,不斷從另一側拉電子。

為什麼我們關心一方比另一方更多的電子?因為模擬世界是混亂的,會造成各種不良影響。要採取一個:它可以對低通濾波器中使用的電容器進行充電,從而在訊號電平本身產生偏移,最終導致位錯誤。這些錯誤將需要時間來積累,但是我們不希望我們的網路裝置在兩年的正常執行時間之後突然破壞資料,就因為我們碰巧傳送了1比0多的二進位制數。(電子相關術語在這裡結束。)

通過使用8b / 10b編碼,乙太網可以平衡通過線路傳送的0和1數量,即使我們傳送大多數為1或大多數為0的資料。硬體跟蹤0到1的比例,將溢位的8位位元組對映到10位表的不同選項,以實現電氣平衡。(較新的乙太網標準,如10 GB乙太網,使用不同的和更復雜的編碼系統。)

我們就討論到這裡,因為我們已經超出了可以被視為程式設計的範圍,但是還有更多的協議來適應物理層。在許多情況下,硬體問題的解決方案在於軟體本身,如在用於校正DC偏移的8b / 10b編碼的情況下。這對程式設計師來說可能有點令人不安:我們喜歡假裝我們的軟體生活在一個完美的柏拉圖式世界,沒有粗俗的物理缺陷。實際上,一切都是模擬的,並且限制著每個人的工作和軟體的複雜性。

  網際網路絡棧

網際網路協議最好被理解為層堆疊。乙太網提供物理資料傳輸和兩個點對點裝置之間的連結。IP提供了定址層,允許路由器和大型網路存在,但它是無連線的。分組資料被髮射到乙太網中,但沒有指示是否到達的功能。TCP通過使用序列號,通過確認和重傳來新增一層可靠的傳輸。

最後,像HTTP這樣的應用層協議在TCP之上。在這個層面上,我們能夠定址,並且似乎實現了可靠傳輸和持續連線。IP和TCP將應用程式開發人員從不斷重新實現資料包重傳和定址等工作中解放出來。

這些層的獨立性很重要。例如,當我的88.5 MB視訊傳輸丟包時,網際網路的骨幹路由器不知道,只有我的機器和網路伺服器知道。來自我計算機的數十個重複ACK都被完全路由在丟失原始資料包的相同路由基礎設施上。負責丟棄丟包路由器的也可能是幾毫秒後更換的路由器。這是理解網際網路的重要一點:路由基礎架構並不瞭解TCP,它只有路線。(雖然有一些例外,但通常是這樣的。)

協議棧的層獨立執行,但它們不是獨立設計的。更高級別的協議往往建立在較低級別的協議上:HTTP建立在基於IP的TCP上,IP建立在乙太網上。較低層次的設計決策往往影響更高層次的決策,甚至影響數十年後的決策。

乙太網是傳統的,涉及到物理層,所以它需要設定基本引數。乙太網有效載荷最多為1500位元組。

IP包需要配置在乙太網幀中。IP具有20位元組的最小報頭,因此IP資料包的最大有效負載為1,500 - 20 = 1,480位元組。

同樣,TCP分組需要適應IP分組。TCP也具有20位元組的最小報頭,最大TCP有效負載為1,480 - 20 = 1,460位元組。實際上,其他header和協議可以進一步減少。1,400是一個保守的TCP有效載荷大小。

1,400位元組的限制影響現代協議的設計。例如,HTTP請求通常很小,如果我們將它們分配於一個數據包而不是兩個資料包,那麼我們可以減少丟失部分請求的可能性,同時可以減少TCP重傳的可能性。為了從HTTP請求中擠出位元組,HTTP / 2指定了標頭檔案的壓縮,這通常很小。沒有來自TCP,IP和乙太網的上下文,這似乎很愚蠢:為什麼新增到協議的header壓縮來只儲存幾個位元組?因為按照HTTP / 2規範在第2節的介紹,壓縮允許“許多請求被壓縮成一個數據包”。

HTTP / 2的header壓縮是為了滿足來自IP約束的TCP限制,這些限制來自於20世紀70年代開發的乙太網中的約束,1980年開始實施,並於1983年進行了標準化。

最後一個問題:為什麼乙太網有效載荷大小設定為1500位元組?沒有深刻的理由,這只是一個很好的折衷點。每幀需要42位元組的非有效載荷資料。如果有效載荷最大值只有100位元組,那麼只有70%(100/142)的時間用於傳送有效載荷。有效載荷為1500位元組意味著大約有97%(1500/1542)的時間用於傳送有效載荷,這是一個很好的效率水平。將資料包大小提高較多的話,則裝置需要較大的緩衝區,我們還是很難獲得另外高1%或2%的效率。簡而言之:HTTP / 2的header壓縮是由於20世紀70年代後期網路裝置的RAM限制而導致的。

熱門標籤