jojoma· 發(fā)布于Ruby China --?
緣起是我想了解一下底層網(wǎng)絡(luò)的原理,看了幾天《TCP/IP 詳解 卷一》屋匕,但是這部書讀起來十分吃力观腊。這時候正好看到hn談到這篇Network protocols名船。所以特地翻譯過來诈茧,希望也能有人從中受益。本人知識有限纺念,在一些譯文處補(bǔ)充了原文用詞來輔助理解贝椿,翻譯不對之處歡迎指正。
網(wǎng)絡(luò)棧技術(shù)柠辞,完成了幾件看起來不可能的任務(wù):它在不可靠網(wǎng)絡(luò)基礎(chǔ)上团秽,實(shí)現(xiàn)了可靠數(shù)據(jù)傳輸,傳輸過程中鮮有可察覺的問題出現(xiàn)叭首。它在網(wǎng)絡(luò)擁塞時能夠平滑適應(yīng)习勤。它給網(wǎng)絡(luò)中上十億的活動節(jié)點(diǎn)提供地址。它能在受損的網(wǎng)絡(luò)基礎(chǔ)設(shè)施中焙格,往正確的路線發(fā)送數(shù)據(jù)包图毕,即使是亂序到達(dá)在接收端,也能將數(shù)據(jù)包重新裝配成正確的順序眷唉。它適應(yīng)了深奧的模擬analog硬件需求予颤,比如以太網(wǎng)電纜兩端的電荷平衡。網(wǎng)絡(luò)技術(shù)工作得如此之好冬阳,以至于網(wǎng)絡(luò)用戶們從沒聽說過它們蛤虐,甚至大部分編寫程序的工程師們也不知道底層究竟是如何工作的。
網(wǎng)絡(luò)路由
在古老的模擬電話年代肝陪,打電話意味著建立一個連接你和你朋友的電話的驳庭、持續(xù)的、電子連接氯窍。仿佛真的有根電話線饲常,直接在你和朋友之間工作。當(dāng)然實(shí)際沒有這根線——電話連接經(jīng)過了復(fù)雜的交換系統(tǒng)——但是這個連接電子上等效于一根線狼讨。
互聯(lián)網(wǎng)的節(jié)點(diǎn)數(shù)太多了贝淤,不能套用這個方式。我們不可能給每一臺機(jī)器與另一臺機(jī)器都建立一個直接連接的政供、不被打斷的路線用于通信播聪。
相應(yīng)地朽基,數(shù)據(jù)是由一個路由器發(fā)送給下一個路由器,每次傳輸都使數(shù)據(jù)離目的地更近一步犬耻,整個鏈?zhǔn)絺鬟f過程像傳桶隊列 Bucket-brigade踩晶。舉個例子执泰,從我的筆記本到 google.com 之間枕磁,途徑的每個路由器都連接著許多其他路由器,各自維護(hù)著一組不精確的路由表术吝,路由表表示出哪些路由器更靠近互聯(lián)網(wǎng)的哪一部分计济。當(dāng)一組目的地是 google.com 的數(shù)據(jù)包到達(dá)時,路由器在路由表上進(jìn)行快速查找排苍,并發(fā)送數(shù)據(jù)包到更靠近谷歌的地方沦寂。數(shù)據(jù)包很小,所以傳輸鏈上路由器之間的數(shù)據(jù)傳遞耗時也極短淘衙。
路由可以拆分成兩個子問題传藏。第一個是,地址:數(shù)據(jù)的目的地用什么表示彤守?這個由IP 協(xié)議毯侦,其中的IP地址來處理。IPv4具垫,作為最廣泛使用的IP版本侈离,提供的地址空間只有32位,已經(jīng)全部被分配筝蚕,所以添加一個新的節(jié)點(diǎn)到公開的互聯(lián)網(wǎng)只能重用已存在的IP地址卦碾。IPv6,允許使用 2 ^ 128 個地址(大約 10 ^ 38)起宽,在2017年只有20%左右的采用率洲胖。
既然已經(jīng)解決了地址的問題,我們現(xiàn)在需要知道如何在互聯(lián)網(wǎng)上路由數(shù)據(jù)包到其目的地坯沪。路由是非陈逃常快的,沒有時間在遠(yuǎn)端數(shù)據(jù)庫中查詢路由信息(所以只能在本地)屏箍。這速度有多快呢绘梦?舉個例子, Cisco ASR 9922 路由器擁有著每秒最大160TB的處理能力赴魁。假設(shè)數(shù)據(jù)包是完整的1.5KB(12000位bit)卸奉,那么每秒有133億個數(shù)據(jù)包流經(jīng)這個19英寸小機(jī)器。
為了快速路由颖御,路由器維護(hù)著指示著到達(dá)其他IP地址組路徑的路由表榄棵。當(dāng)一個新的數(shù)據(jù)包到達(dá)時凝颇,路由器查詢路由表,告知這個數(shù)據(jù)包最接近目的地的路由器疹鳄。這個路由器會把數(shù)據(jù)包發(fā)送到下一個路由器拧略,然后再往下一個發(fā)送。BGP協(xié)議的工作就是瘪弓,在不同路由器之間溝通垫蛆,保證路由表是最新的。
轉(zhuǎn)換成數(shù)據(jù)包packet switching
如果互聯(lián)網(wǎng)的工作方式是腺怯,路由器互相之間沿著線路傳遞數(shù)據(jù)袱饭,那如果數(shù)據(jù)很大會發(fā)生什么?比如說呛占,如果我們請求一個88.5MB的視頻The Birth & Death of JavaScript虑乖。
我們可以試試設(shè)計一個網(wǎng)絡(luò),在這當(dāng)中88.5MB的文件直接由網(wǎng)絡(luò)服務(wù)器發(fā)送給第一個路由器晾虑,然后第二個疹味,如此下去。不幸的是帜篇,這樣的網(wǎng)絡(luò)不可能在互聯(lián)網(wǎng)級別的規(guī)模下工作糙捺,甚至內(nèi)網(wǎng)規(guī)模下都很難。
首先坠狡,計算機(jī)的存儲量是有限的继找。如果一個給定的路由只有88.4MB的可用緩存,那它就不能存儲這個88.5MB的視頻文件逃沿。這個數(shù)據(jù)會被直接丟棄婴渡,甚至更糟,我完全不知道這件事的發(fā)生凯亮。如果路由器是如此忙碌以至于丟棄了數(shù)據(jù)之后边臼,都沒時間告訴我它丟棄了數(shù)據(jù)。
其次假消,計算機(jī)都是不可靠的柠并。有時,路由節(jié)點(diǎn)崩潰富拗。有時臼予,船只的??意外損壞水下光纜,導(dǎo)致互聯(lián)網(wǎng)一大部分不可訪問啃沪。
基于這些提到的以及更多原因粘拾,我們不會在互聯(lián)網(wǎng)中傳遞88.5MB大小的消息。相反创千,我們把數(shù)據(jù)拆分成許多數(shù)據(jù)包缰雇,大小通常在1.4KB左右入偷。我們的視頻文件將被拆分成63214左右個分隔的數(shù)據(jù)包用于傳輸。
亂序數(shù)據(jù)包
使用抓包工具Wireshark觀察The Birth & Death of JavaScript的一次真實(shí)傳輸械哟,我能看到接收了一共 61807 個數(shù)據(jù)包疏之,每個 1432 字節(jié)。兩者相乘暇咆,我們得到88.5MB锋爪,這正是視頻文件的大小。(這不包括其他協(xié)議的開支糯崎,如果包含的話几缭,數(shù)字會更大些)
這次傳輸是基于HTTP河泳,一種基于TCP的協(xié)議沃呢。傳輸花了 14 秒,所以平均每秒有 4400 個數(shù)據(jù)包到達(dá)拆挥,或者說每個數(shù)據(jù)包花了250毫秒到達(dá)薄霜。在這14秒中,我的計算機(jī)接收了所有 61807 個數(shù)據(jù)包纸兔,也許不是按順序接收惰瓜,在接收過程中進(jìn)行重新裝配成完整文件。
TCP數(shù)據(jù)包重新組裝使用的是一種可想象的最簡單的機(jī)制:計時器汉矿。每個數(shù)據(jù)包在發(fā)送時都被賦予一個序列號崎坊。在接收端,數(shù)據(jù)包按序列號排列洲拇。一旦他們?nèi)颗藕庙樞蚰巫幔瑳]有間隔,我們就知道整個文件都接收到了沒有丟失赋续。
(真實(shí)情況下男翰,TCP序列號并非是每次增加一的整數(shù),但這個細(xì)節(jié)在本文中并不重要纽乱。)
就算如此蛾绎,那我們怎么知道什么時候文件接收完成呢?TCP對此一無所知鸦列,這個是更高級別協(xié)議的職責(zé)租冠。舉個例子,HTTP響應(yīng)response中包含一個叫做Content-Length的頭部薯嗤,說明了返回響應(yīng)的總長度顽爹。客戶端讀取這個頭字段应民,然后一直讀取TCP數(shù)據(jù)包话原,重新裝配它們夕吻,直到達(dá)到了此頭字段指定的數(shù)據(jù)大小。這是為什么HTTP頭部(以及其他大多數(shù)協(xié)議的頭部)比響應(yīng)載荷payload率先到達(dá)的原因之一繁仁,否則我們都不能知曉載荷的大小涉馅。
當(dāng)我們在說客戶端的時候,我們實(shí)際在說整個接收數(shù)據(jù)的計算機(jī)黄虱。TCP組裝是在內(nèi)核中完成的稚矿,所以瀏覽器、curl和wget這樣的應(yīng)用不需要手動重新裝配TCP數(shù)據(jù)包捻浦。但是內(nèi)核不處理HTTP晤揣,所以應(yīng)用需要理解Content-Length頭字段并知曉需要讀取多少字節(jié)。
有了序列號和數(shù)據(jù)包重新排序朱灿,我們能傳輸大量數(shù)據(jù)昧识,即使數(shù)據(jù)包是亂序的。但如果一個數(shù)據(jù)包在傳輸中丟失了盗扒,在HTTP響應(yīng)中留下一個空洞怎么辦跪楞?
傳輸窗口transmission winsow與慢啟動slow start
我開著Wireshark下載了The Birth & Death of JavaScript。查看抓包記錄侣灶,我能看到數(shù)據(jù)包一個接一個被成功接收甸祭。
舉個例子,一個序列號為 563321 的數(shù)據(jù)包到達(dá)了褥影。像所有TCP數(shù)據(jù)包一樣池户,它包含了一個“下一個包序號”,指示著接下來一個數(shù)據(jù)包的序列號凡怎。這個包的“下一個包序號”是 564753校焦。傳輸過程中下一個數(shù)據(jù)包,的序列號確實(shí)是 564753栅贴,所以一切正常斟湃。這發(fā)生了數(shù)千次,隨著連接開始加速傳輸數(shù)據(jù)檐薯。
有時候凝赛,我的計算機(jī)發(fā)出一條消息給服務(wù)器說,打個比方坛缕,“我已經(jīng)接收了包序號小于或等于 564753 的所有數(shù)據(jù)包墓猎。”這稱為ACK赚楚,確認(rèn)acknowledgement的簡寫毙沾,我的計算機(jī)確認(rèn)接收服務(wù)器的數(shù)據(jù)包。在一個新的連接中宠页,Linux內(nèi)核每接收10個數(shù)據(jù)包后左胞,就發(fā)出一個ACK寇仓。數(shù)字 10 由常數(shù)TCP_INIT_CWND控制,常數(shù)在內(nèi)核源碼中被定義烤宙。
(TCP_INIT_CWND里的 CWND 表示 擁塞窗口 congestion window:同一時刻可以傳輸?shù)臄?shù)據(jù)總大小遍烦。)如果網(wǎng)絡(luò)變得擁塞(超負(fù)荷),窗口大小減小躺枕,從而減慢數(shù)據(jù)包的傳輸服猪。
十個數(shù)據(jù)包是大約14KB,所以一開始的速度限制是14KB拐云。這是TCP慢啟動的部分:連接建立時擁塞窗口很小罢猪。如果沒有數(shù)據(jù)包丟失,接受者將持續(xù)增加擁塞窗口叉瘩,允許同時傳輸更多數(shù)據(jù)包膳帕。
最終,將會有數(shù)據(jù)包丟失房揭,所以接收窗口會減小备闲,減慢傳輸。像這樣自動調(diào)整擁塞窗口捅暴,以及其他參數(shù),數(shù)據(jù)發(fā)送者和接收者讓數(shù)據(jù)傳輸最大化利用網(wǎng)絡(luò)帶寬咧纠。
這發(fā)生在連接的兩端:每端都發(fā)出ACK確認(rèn)消息蓬痒,也維護(hù)各自的擁塞窗口。不對稱窗口允許協(xié)議用不對稱的上下行帶寬漆羔,最大化利用網(wǎng)絡(luò)連接梧奢,就像大多數(shù)住宅區(qū)和移動網(wǎng)絡(luò)連接一樣。
可靠傳輸
計算機(jī)是不可靠的演痒,由計算機(jī)組成的網(wǎng)絡(luò)更加不可靠亲轨。在像互聯(lián)網(wǎng)這樣的大規(guī)模網(wǎng)絡(luò)中,失敗是操作中常見的一部分鸟顺,并且必須得到良好處理惦蚊。在一個數(shù)據(jù)包網(wǎng)絡(luò)中,這意味著重傳:如果客戶端接收了序號1和3的數(shù)據(jù)包讯嫂,但沒有接收到2蹦锋,那么它需要要求服務(wù)器重新發(fā)出丟失的數(shù)據(jù)包。
當(dāng)每秒接收上千數(shù)據(jù)包時欧芽,比如下載我們的88.5MB視頻時莉掂,錯誤幾乎百分之百會產(chǎn)生。為了給大家展示千扔,讓我們打開Wireshark憎妙。很多數(shù)據(jù)包接收库正,一切看起來很正常。每個數(shù)據(jù)包都有一個“下一個包序號”厘唾,緊接著一個帶著這個序號的數(shù)據(jù)包诀诊。
突然問題出現(xiàn)了。第 6269 個數(shù)據(jù)包的“下一個包序號”是 7208745阅嘶,但那個數(shù)據(jù)包并沒有到達(dá)属瓣。相反,序列號為 7211609 的數(shù)據(jù)包到達(dá)了讯柔。這是一個亂序數(shù)據(jù)包:有東西丟失了抡蛙。
我們很難說出究竟什么出了問題。也許互聯(lián)網(wǎng)中的一個中間路由器超負(fù)荷了魂迄,也許是我的本地路由器超負(fù)荷了粗截。也許有人打開了微波爐,產(chǎn)生了電磁干擾捣炬,減慢了我的無線連接熊昌。無論如何,這個數(shù)據(jù)包丟失了湿酸,唯一的跡象是意外接收到的數(shù)據(jù)包婿屹。
TCP并沒有特別的“我丟失了一個數(shù)據(jù)包”消息。相反推溃,ACK消息會被巧妙地復(fù)用來表明數(shù)據(jù)丟失昂利。任何亂序的數(shù)據(jù)包,會導(dǎo)致接收者重復(fù)確認(rèn)最后的“正確的”數(shù)據(jù)包——正確順序的最后一個铁坎。實(shí)際上蜂奸,接收者說的是:“我確認(rèn)接收到了數(shù)據(jù)包5。在那之后我也接收到了別的數(shù)據(jù)硬萍,但我知道那不是數(shù)據(jù)包6扩所,因為它并不匹配數(shù)據(jù)包5的下一個包序號∑庸裕”
如果只是兩個數(shù)據(jù)包在傳輸時調(diào)換了順序祖屏,這會導(dǎo)致一次額外的ACK,等到亂序數(shù)據(jù)包接收到之后一切就能正常繼續(xù)下去寒砖。但是如果有個數(shù)據(jù)包真的丟失了赐劣,意外數(shù)據(jù)包將會一直到達(dá),因而接收者會持續(xù)發(fā)出重復(fù)的哩都、最后一個正常數(shù)據(jù)包的ACK消息魁兼。這會導(dǎo)致上百個重復(fù)的ACK消息。
當(dāng)數(shù)據(jù)發(fā)送者一下看到三個重復(fù)ACK消息,它就假定緊接著的數(shù)據(jù)包丟失了咐汞,并進(jìn)行重新傳輸盖呼。這被稱為TCP快速重傳,因為它比以前的基于超時的做法要快一些化撕。有趣的是几晤,協(xié)議自身不會顯式地去說“請立即重傳這個消息!”相反植阴,多個ACK消息從協(xié)議自然產(chǎn)生蟹瘾,作為重傳的觸發(fā)器。
(一個有意思的思維實(shí)驗:如果一部分重復(fù)的ACK消息也丟失了掠手,沒能到達(dá)數(shù)據(jù)發(fā)送者憾朴,會發(fā)生什么?)
-- 重傳甚至在網(wǎng)絡(luò)正常工作時都十分常見喷鸽。在對下載88.5MB視頻進(jìn)行抓包的過程中众雷,我看到了
-- 因為持續(xù)性成功傳輸,擁塞窗口迅速增大到了將近1MB做祝。
-- 數(shù)千數(shù)據(jù)包按順序出現(xiàn)砾省,一切正常。
-- 一個數(shù)據(jù)包到達(dá)順序不正確混槐。
-- 數(shù)據(jù)繼續(xù)以每秒幾MB的速度涌入编兄,但丟失的數(shù)據(jù)包依舊沒出現(xiàn)。
-- 我的計算機(jī)發(fā)出了不少重復(fù)的最后正常數(shù)據(jù)包的ACK消息纵隔,但內(nèi)核也存下待處理的亂序數(shù)據(jù)包翻诉,以備后續(xù)的重新組裝。
-- 服務(wù)器接收到了重復(fù)的ACK捌刮,并重新發(fā)送了丟失的數(shù)據(jù)包
-- 我的客戶端發(fā)出之前丟失的數(shù)據(jù)包,以及后續(xù)數(shù)據(jù)包的確認(rèn)接收的消息舒岸。簡單確認(rèn)最近的數(shù)據(jù)包即可绅作,它會隱式地確認(rèn)之前所有的數(shù)據(jù)包都被接收。
-- 傳輸繼續(xù)蛾派,但由于丟失的數(shù)據(jù)包俄认,擁塞窗口變小了。
這就是正常情況洪乍,這些在每次我對完整下載進(jìn)行抓包時都會產(chǎn)生眯杏。TCP在自己的職責(zé)上做得是如此出色,以至于我們在日常使用中從沒考慮過網(wǎng)絡(luò)是不可靠的壳澳,盡管在正常情況下網(wǎng)絡(luò)都會例行性地失敗岂贩。
物理網(wǎng)絡(luò)
所有這些網(wǎng)絡(luò)數(shù)據(jù),都必須通過像銅線巷波、光纜萎津、無線電這樣的物理媒介進(jìn)行傳輸卸伞。而在物理層協(xié)議之中,以太網(wǎng)最為著名锉屈。它在互聯(lián)網(wǎng)興起之初的流行荤傲,導(dǎo)致了我們在設(shè)計其他協(xié)議的時候必須適應(yīng)它的局限。
首先颈渊,讓我們把物理細(xì)節(jié)弄清楚遂黍。以太網(wǎng)與 RJ45 接頭關(guān)系最緊密,后者看起來像更大的八針eight-pin版本的四針手機(jī)插孔four-pin phone jacks俊嗽。以太網(wǎng)也連接著cat5(或cat5e雾家,或cat6,或cat7)電纜乌询,該電纜包含了擰成4對的8根電線榜贴。其他媒介也存在,但我們在家中最有可能遇到的就是這些:裹在保護(hù)套下的8根電線妹田,以及與之相連的8針插頭唬党。
以太網(wǎng)是一個物理層協(xié)議:描述了位信息如何轉(zhuǎn)換成電線中的數(shù)字信號。它也是一個鏈路link層協(xié)議:描述了兩個節(jié)點(diǎn)之間的直接連接鬼佣。然而驶拱,這是單純的點(diǎn)對點(diǎn),對網(wǎng)絡(luò)中數(shù)據(jù)是如何路由的并不關(guān)心晶衷。以太網(wǎng)這里沒有TCP連接中的連接概念蓝纲,也沒有IP地址中的可重新分配的地址概念。
作為一個協(xié)議晌纫,以太網(wǎng)有兩個主要的工作税迷。第一,每個設(shè)備需要意識到它連接著一些東西锹漱,并且連接速度這樣的參數(shù)需要協(xié)商箭养。
第二,一旦鏈路link建立哥牍,以太網(wǎng)需要攜帶信息毕泌。像更高層次的TCP和IP協(xié)議一樣,以太網(wǎng)的數(shù)據(jù)也拆分成數(shù)據(jù)包嗅辣。數(shù)據(jù)包的核心是數(shù)據(jù)幀撼泛,幀有1.5KB的載荷,外加22字節(jié)的頭部信息澡谭。頭部信息中包含源MAC地址和目的地MAC地址愿题,載荷長度,以及校驗和checksum這樣的信息。這些字段令人熟悉:工程師常常需要處理地址抠忘、長度以及校驗和撩炊,我們也知道為什么它們是必須的。
數(shù)據(jù)幀接著被其他層的頭數(shù)據(jù)包裹起來崎脉,構(gòu)造出完整的數(shù)據(jù)包拧咳。這些頭部數(shù)據(jù)很...奇怪。它們已經(jīng)開始和模擬電路系統(tǒng)的底層現(xiàn)實(shí)發(fā)生碰撞了囚灼,所以我們絕不想把這些數(shù)據(jù)放到軟件協(xié)議中去骆膝。一個完整的以太網(wǎng)數(shù)據(jù)包包含:
-- 序言preamble,由56位交替的0和1構(gòu)成(7字節(jié))灶体。設(shè)備使用這個來同步時鐘阅签,有點(diǎn)像人們數(shù)數(shù)發(fā)令“1-2-3-開始!”計算機(jī)不能數(shù)數(shù)超過1蝎抽,所以他們通過說“10101010101010101010101010101010101010101010101010101010”來同步
-- 一個8位(1字節(jié))起始幀分隔符政钟,通常是十進(jìn)制數(shù)字171(二進(jìn)制表示是10101011)。它標(biāo)識了序言的結(jié)尾樟结,注意分隔符中開始還在重復(fù)“10”养交,直到末尾有個“11”。
-- 核心數(shù)據(jù)幀瓢宦,包含了源地址碎连、目標(biāo)地址、載荷等等驮履,如前所述鱼辙。
-- 一個96位(12字節(jié))的數(shù)據(jù)包間隔,其中的行是留空的玫镐。大膽猜測一下倒戏,這是留給設(shè)備休息的,因為它們很累了恐似。
總結(jié)一下上面:我們想要傳輸1.5KB數(shù)據(jù)峭梳。我們添加22字節(jié)的包含源地址、目標(biāo)地址蹂喻、數(shù)據(jù)大小以及校驗和的頭信息以創(chuàng)建數(shù)據(jù)幀。我們再添加額外的22字節(jié)的數(shù)據(jù)捂寿,為了適應(yīng)硬件需求口四,這些構(gòu)成了完整的以太網(wǎng)數(shù)據(jù)包。
你也許會以為以太網(wǎng)已經(jīng)是網(wǎng)絡(luò)技術(shù)棧的最底層了秦陋。不是這樣蔓彩,但事情確實(shí)變得更奇怪了,因為模擬世界的對技術(shù)的影響更甚 pokes through even more。
現(xiàn)實(shí)世界中的網(wǎng)絡(luò)
數(shù)字系統(tǒng)并不存在赤嚼,一切都是模擬的旷赖。
假設(shè)我們有一個5伏特 CMOS 系統(tǒng),(CMOS是一種數(shù)字系統(tǒng)更卒,不熟悉也沒關(guān)系等孵。)這意味著,完全開啟fully-on的信號將是5伏特蹂空,完全關(guān)閉的信號是0伏特俯萌。但是沒有信號是完全開閉的,物理世界不這樣工作上枕。實(shí)際上咐熙,我們的5伏特 CMOS 系統(tǒng),會把任何高于1.67伏特的信號看做1辨萍,低于1.67伏特的信號看做0棋恼。
(1.67是5的1/3。我們不用關(guān)心為什么分界線在1/3锈玉。當(dāng)然如果你想深究爪飘,這里有維基百科說明。另外嘲玫,以太網(wǎng)不是CMOS悦施,甚至跟CMOS都沒有關(guān)系,但CMOS和它的1/3分界線能用來做一個簡單說明make for a simple illustration)
我們的以太網(wǎng)數(shù)據(jù)包必須經(jīng)由一條物理線去团,也就是改變電線中的電壓抡诞。以太網(wǎng)是一個5伏特的系統(tǒng),所以我們會天真地以為土陪,以太網(wǎng)協(xié)議中的1位bit是電線中的5伏特昼汗,0位是0伏特。但是有兩個問題:首先鬼雀,電壓范圍是-2.5伏特到+2.5伏特顷窒。其次,更奇怪的是源哩,每組8位信息在到達(dá)電線之前鞋吉,都會被拓展成10位。
8位可以有256種取值励烦,10位有1024種取值谓着,所以可以想象有張表在它們之間映射。每個8位的字節(jié)能被映射成4種10字節(jié)的信息坛掠,后者到達(dá)接收終點(diǎn)之后會被還原成同一個8位字節(jié)赊锚。舉個例子治筒,10位的值 00.0000.0000 也許映射到 8位 0000.0000。但是也許 10位值 10.1010.1010 也能映射到同一個8位字節(jié)舷蒲。當(dāng)以太網(wǎng)設(shè)備不管看到 00.0000.0000 還是 10.1010.1010耸袜,它都能理解這是字節(jié)0(二進(jìn)制 0000.0000)。
(警告:下面可能需要一些電子電路知識)
上面這種映射的存在牲平,是為了服務(wù)一個極其模擬的需求 extremely analog need:平衡設(shè)備中的電壓堤框。假設(shè)這種8位到10位的編碼不存在,并且我們需要發(fā)送的數(shù)據(jù)恰好都是二進(jìn)制1欠拾。以太網(wǎng)的電壓范圍是-2.5伏特到+2.5伏特胰锌,所以我們會使以太網(wǎng)線的電壓維持在+2.5伏特,繼而一直從線的另一端吸引電子過來pulling electrons藐窄。
為什么我們要關(guān)心一端從另一端獲取電子呢资昧?因為模擬世界是混亂的,可能會產(chǎn)生各種各樣意外的影響荆忍。舉個例子格带,這樣會給低通濾波器中使用的電容器充電,使得信號電平中產(chǎn)生偏移刹枉,最終導(dǎo)致位錯誤叽唱。這些錯誤需要時間積累,但我們顯然不希望微宝,僅僅因為我們傳輸?shù)亩M(jìn)制1比0多棺亭,兩年之后網(wǎng)絡(luò)設(shè)備中突然開始產(chǎn)生數(shù)據(jù)錯誤。
(有關(guān)電子電路的說到這里)
通過使用8位-10位 編碼蟋软,以太網(wǎng)能保持電線中的0和1的平衡镶摘,即使我們要發(fā)送的數(shù)據(jù)都是1或者都是0。硬件會檢測0和1的比例岳守,映射要發(fā)送的8位字節(jié)到不同的10位信號凄敢,以達(dá)到維持電荷平衡。(新的以太網(wǎng)標(biāo)準(zhǔn)湿痢,如10GB以太網(wǎng)涝缝,使用不同的更復(fù)雜的編碼系統(tǒng))
到此打住,因為我們談?wù)摰囊呀?jīng)超出編程的范圍了譬重,但是必須要說明的是拒逮,還有更多協(xié)議相關(guān)問題是為了適應(yīng)物理層。在許多情況下臀规,解決硬件問題的方法消恍,都在軟件中實(shí)現(xiàn),比如上文使用8位-10位編碼來修正直流偏移DC offset以现。這對我們這樣的工程師來說可能有點(diǎn)尷尬:我們習(xí)慣于假裝軟件生活在一個完美的柏拉圖式的世界中狠怨,沒有物理上庸俗的缺陷devoid of the vulgar imperfections of physicality。事實(shí)上邑遏,一切都是模擬的佣赖,適應(yīng)這種復(fù)雜性是每個人的工作,當(dāng)然也包括軟件记盒。
相互聯(lián)接的網(wǎng)絡(luò)棧
互聯(lián)網(wǎng)協(xié)議族最好理解為一組層的集合憎蛤。以太網(wǎng)提供物理數(shù)據(jù)傳輸以及兩個點(diǎn)對點(diǎn)設(shè)備之間的鏈路。IP提供了地址層纪吮,允許路由器和大規(guī)模網(wǎng)絡(luò)的存在俩檬,但是是無連接的,數(shù)據(jù)包雙向傳輸卻無從判斷是否到達(dá)碾盟。TCP通過使用序列號棚辽、確認(rèn)以及重傳,添加了可靠的傳輸層冰肴。
最終屈藐,應(yīng)用層協(xié)議如HTTP建立在TCP之上。在這一層熙尉,我們已經(jīng)有了地址联逻,以及可靠傳輸和持續(xù)連接的幻覺illusion。IP和TCP將應(yīng)用開發(fā)者检痰,從重復(fù)實(shí)現(xiàn)數(shù)據(jù)包重傳包归、地址處理等等的地獄中拯救出來。
這些層的獨(dú)立性是十分重要的铅歼。舉個例子公壤,當(dāng)傳輸88.5MB視頻有數(shù)據(jù)包丟失的時候,互聯(lián)網(wǎng)的網(wǎng)絡(luò)中樞路由器并不知道谭贪;只有我的計算機(jī)和網(wǎng)絡(luò)服務(wù)器知道境钟。這個弄丟了原始數(shù)據(jù)包的路由基礎(chǔ)設(shè)施,還在盡職地將我計算機(jī)發(fā)出的許多重復(fù)的ACK消息路由到目的地去俭识。有可能就是同一個路由器慨削,弄丟了數(shù)據(jù)包,幾毫秒之后又帶著重發(fā)的數(shù)據(jù)包來了套媚。這是理解互聯(lián)網(wǎng)的一個重點(diǎn):路由基礎(chǔ)設(shè)施對TCP一無所知缚态;它做的僅僅是路由。(當(dāng)然這也有例外堤瘤,不過大多數(shù)情況下就是這樣)
不同層的協(xié)議獨(dú)立工作玫芦,但它們不是分開獨(dú)立設(shè)計的枣申。高層次協(xié)議通常建立在低層次協(xié)議基礎(chǔ)上庞溜,HTTP建立在TCP上潮太,TCP建立在IP上蜀漆,IP建立在以太網(wǎng)上。更底層的設(shè)計決策老虫,即使在幾十年之后叶骨,也會影響到更高層次的決策。
以太網(wǎng)是古老的祈匙,且涉及物理層忽刽,所以它的需求設(shè)置了基本參數(shù)。一個以太網(wǎng)載荷最大是1.5KB夺欲。
IP數(shù)據(jù)包需要包含于以太網(wǎng)數(shù)據(jù)幀中跪帝。IP的最小頭部大小是20字節(jié),所以IP數(shù)據(jù)包的最大載荷是 1500 - 20 = 1480 字節(jié)些阅。
同樣伞剑,TCP數(shù)據(jù)包需要包含于IP數(shù)據(jù)包中。TCP的最小頭部大小也是20字節(jié)扑眉,所以TCP的最大載荷是 1480 - 20 = 1460 字節(jié)纸泄。在現(xiàn)實(shí)中,其他頭部和協(xié)議會占據(jù)更多空間腰素,保守估計TCP的載荷大小是1400字節(jié)聘裁。
1400字節(jié)的限制影響了現(xiàn)代協(xié)議的設(shè)計。舉個例子弓千,HTTP請求通常很小衡便。如果我們把它們?nèi)M(jìn)一個數(shù)據(jù)包而不是兩個,就能減小丟失請求某部分的可能洋访,從而減少需要TCP重傳的可能镣陕。為了從小請求中擠出每個字節(jié),HTTP/2指定了頭部壓縮姻政,頭部通常很小呆抑。沒有TCP、IP和以太網(wǎng)的情境的話汁展,這看起來很不明智:為什么要壓縮一個協(xié)議的頭部鹊碍,僅僅為了節(jié)約幾字節(jié)大小的空間?因為食绿,正如 HTTP/2 規(guī)范在第2節(jié)的介紹中所說侈咕,壓縮允許“多個請求被壓縮成一個數(shù)據(jù)包”。
HTTP/2 的頭部壓縮是為了適應(yīng)TCP的限制器紧,這個限制來自IP的限制耀销,再往上來自以太網(wǎng)的限制。而以太網(wǎng)在上世紀(jì)70年代發(fā)展起來的铲汪,1980年投入商用熊尉,并在1983年標(biāo)準(zhǔn)化罐柳。
最后一個問題:為什么以太網(wǎng)的載荷大小設(shè)置在1500字節(jié)(1.5KB)呢?其實(shí)沒有深層次的原因:只是一個很好的權(quán)衡考量帽揪。每個數(shù)據(jù)幀中有42字節(jié)大小的非載荷數(shù)據(jù)硝清。如果載荷的最大值只有100字節(jié),那么只有70%(100/142)的時間花在發(fā)送載荷上转晰。而1500字節(jié)大小的載荷,意味著大約97%(1500/1542)的時間用于發(fā)送載荷士飒,這樣的效率是可觀的查邢。再增加數(shù)據(jù)包的大小的話,會需要設(shè)備擁有更大的緩沖區(qū)酵幕,這使得再提高一兩個百分比的效率變得十分困難扰藕。簡而言之,20世紀(jì)70年代末網(wǎng)絡(luò)設(shè)備的RAM限制芳撒,導(dǎo)致HTTP/2 引入了頭部壓縮邓深。