從輸入U(xiǎn)RL到頁(yè)面加載完成的過程中都發(fā)生了什么?
寫這篇文章并不是為了幫大家準(zhǔn)備面試,而是想借這道題來介紹計(jì)算機(jī)和互聯(lián)網(wǎng)的基礎(chǔ)知識(shí),讓讀者了解它們之間是如何關(guān)聯(lián)起來的。...
背景 本文來自于之前我發(fā)的一篇微博:
不過寫這篇文章并不是為了幫大家準(zhǔn)備面試,而是想借這道題來介紹計(jì)算機(jī)和互聯(lián)網(wǎng)的基礎(chǔ)知識(shí),讓讀者了解它們之間是如何關(guān)聯(lián)起來的。
為了便于理解,我將整個(gè)過程分為了六個(gè)問題來展開。
第一個(gè)問題:從輸入 URL 到瀏覽器接收的過程中發(fā)生了什么事情?
從觸屏到 CPU
首先是「輸入 URL」,大部分人的第一反應(yīng)會(huì)是鍵盤,不過為了與時(shí)俱進(jìn),這里將介紹觸摸屏設(shè)備的交互。
觸摸屏一種傳感器,目前大多是基于電容(Capacitive)來實(shí)現(xiàn)的,以前都是直接覆蓋在顯示屏上的,不過最近出現(xiàn)了 3 種嵌入到顯示屏中的技術(shù),第一種是 iPhone 5 的 In-cell,它能減小了 0.5 毫米的厚度,第二種是三星使用的 On-cell 技術(shù),第三種是國(guó)內(nèi)廠商喜歡用的 OGS 全貼合技術(shù)。
當(dāng)手指在這個(gè)傳感器上觸摸時(shí),有些電子會(huì)傳遞到手上,從而導(dǎo)致該區(qū)域的電壓變化,觸摸屏控制器芯片根據(jù)這個(gè)變化就能計(jì)算出所觸摸的位置,然后通過總線接口將信號(hào)傳到 CPU 的引腳上。
以 Nexus 5 為例,它所使用的觸屏控制器是Synaptics S3350B,總線接口為I²C,以下是 Synaptics 觸摸屏和處理器連接的示例:
左邊是處理器,右邊是觸摸屏控制器,中間的 SDA 和 SCL 連線就是 I²C 總線接口。
CPU 內(nèi)部的處理
移動(dòng)設(shè)備中的 CPU 并不是一個(gè)單獨(dú)的芯片,而是和 GPU 等芯片集成在一起,被稱為 SoC(片上系統(tǒng))。
前面提到了觸屏和 CPU 的連接,這個(gè)連接和大部分計(jì)算機(jī)內(nèi)部的連接一樣,都是通過電氣信號(hào)來進(jìn)行通信的,也就是電壓高低的變化,如下面的時(shí)序圖:
在時(shí)鐘的控制下,這些電流會(huì)經(jīng)過MOSFET晶體管,晶體管中包含 N 型半導(dǎo)體和 P 型半導(dǎo)體,通過電壓就能控制線路開閉,然后這些 MOSFET 構(gòu)成了CMOS,接著再由 CMOS 實(shí)現(xiàn)「與」「或」「非」等邏輯電路門,最后由邏輯電路門上就能實(shí)現(xiàn)加法、位移等計(jì)算,整體如下圖所示(來自《計(jì)算機(jī)體系結(jié)構(gòu)》):
除了計(jì)算,在 CPU 中還需要存儲(chǔ)單元來加載和存儲(chǔ)數(shù)據(jù),這個(gè)存儲(chǔ)單元一般通過觸發(fā)器(Flip-flop)來實(shí)現(xiàn),稱為寄存器。
以上這些概念都比較抽象,推薦閱讀「How to Build an 8-Bit Computer」這篇文章,作者基于晶體管、二極管、電容等原件制作了一個(gè) 8 位的計(jì)算機(jī),支持簡(jiǎn)單匯編指令和結(jié)果輸出,雖然現(xiàn)代 CPU 的實(shí)現(xiàn)要比這個(gè)復(fù)雜得多,但基本原理還是一樣的。
另外其實(shí)我也是剛開始學(xué)習(xí) CPU 芯片的實(shí)現(xiàn),所以就不在這誤人子弟了,感興趣的讀者請(qǐng)閱讀本節(jié)后面推薦的書籍。
從 CPU 到操作系統(tǒng)內(nèi)核
前面說到觸屏控制器將電氣信號(hào)發(fā)送到 CPU 對(duì)應(yīng)的引腳上,接著就會(huì)觸發(fā) CPU 的中斷機(jī)制,以 Linux 為例,每個(gè)外部設(shè)備都有一標(biāo)識(shí)符,稱為中斷請(qǐng)求(IRQ)號(hào),可以通過/proc/interrupts文件來查看系統(tǒng)中所有設(shè)備的中斷請(qǐng)求號(hào),以下是 Nexus 7 (2013) 的部分結(jié)果:
shell@flo:/$cat/proc/interruptsCPU017:0 GICdg_timer294:1973609 msmgpioelan-ktf3k314:679 msmgpioKEY_POWER
因?yàn)?Nexus 7 使用了 ELAN 的觸屏控制器,所以結(jié)果中的 elan-ktf3k 就是觸屏的中斷請(qǐng)求信息,其中 294 是中斷號(hào),1973609 是觸發(fā)的次數(shù)(手指單擊時(shí)會(huì)產(chǎn)生兩次中斷,但滑動(dòng)時(shí)會(huì)產(chǎn)生上百次中斷)。
為了簡(jiǎn)化這里不考慮優(yōu)先級(jí)問題,以 ARMv7 架構(gòu)的處理器為例,當(dāng)中斷發(fā)生時(shí),CPU 會(huì)停下當(dāng)前運(yùn)行的程序,保存當(dāng)前執(zhí)行狀態(tài)(如 PC 值),進(jìn)入 IRQ 狀態(tài)),然后跳轉(zhuǎn)到對(duì)應(yīng)的中斷處理程序執(zhí)行,這個(gè)程序一般由第三方內(nèi)核驅(qū)動(dòng)來實(shí)現(xiàn),比如前面提到的 Nexus 7 的驅(qū)動(dòng)源碼在這里touchscreen/ektf3k.c。
這個(gè)驅(qū)動(dòng)程序?qū)⒆x取 I²C 總線中傳來的位置數(shù)據(jù),然后通過內(nèi)核的input_report_abs等方法記錄觸屏按下坐標(biāo)等信息,最后由內(nèi)核中的input 子模塊將這些信息都寫進(jìn)/dev/input/event0這個(gè)設(shè)備文件中,比如下面展示了一次觸摸事件所產(chǎn)生的信息:
130|shell@flo:/$getevent-lt/dev/input/event0[414624.658986]EV_ABS ABS_MT_TRACKING_ID 0000835c[414624.659017]EV_ABS ABS_MT_TOUCH_MAJOR 0000000b[414624.659047]EV_ABS ABS_MT_PRESSURE0000001d[414624.659047]EV_ABS ABS_MT_POSITION_X000003f0[414624.659078]EV_ABS ABS_MT_POSITION_Y00000588[414624.659078]EV_SYN SYN_REPORT 00000000[414624.699239]EV_ABS ABS_MT_TRACKING_ID ffffffff[414624.699270]EV_SYN SYN_REPORT 00000000
從操作系統(tǒng) GUI 到瀏覽器
前面提到 Linux 內(nèi)核已經(jīng)完成了對(duì)硬件的抽象,其它程序只需要通過監(jiān)聽/dev/input/event0文件的變化就能知道用戶進(jìn)行了哪些觸摸操作,不過如果每個(gè)程序都這么做實(shí)在太麻煩了,所以在圖像操作系統(tǒng)中都會(huì)包含 GUI 框架來方便應(yīng)用程序開發(fā),比如 Linux 下著名的X。
但 Android 并沒有使用 X,而是自己實(shí)現(xiàn)了一套 GUI 框架,其中有個(gè)EventHub的服務(wù)會(huì)通過epoll方式監(jiān)聽/dev/input/目錄下的文件,然后將這些信息傳遞到 Android 的窗口管理服務(wù)(WindowManagerService)中,它會(huì)根據(jù)位置信息來查找相應(yīng)的 app,然后調(diào)用其中的監(jiān)聽函數(shù)(如 onTouch 等)。
就這樣,我們解答了第一個(gè)問題,不過由于時(shí)間有限,這里省略了很多細(xì)節(jié)。
第二個(gè)問題:瀏覽器如何向網(wǎng)卡發(fā)送數(shù)據(jù)?
從瀏覽器到瀏覽器內(nèi)核
前面提到操作系統(tǒng) GUI 將輸入事件傳遞到了瀏覽器中,在這過程中,瀏覽器可能會(huì)做一些預(yù)處理,比如 Chrome 會(huì)根據(jù)歷史統(tǒng)計(jì)來預(yù)估所輸入字符對(duì)應(yīng)的網(wǎng)站,比如輸入了「ba」,根據(jù)之前的歷史發(fā)現(xiàn) 90% 的概率會(huì)訪問「www.baidu.com 」,因此就會(huì)在輸入回車前就馬上開始建立 TCP 鏈接甚至渲染了,這里面還有很多其它策略,感興趣的讀者推薦閱讀 High Performance Networking in Chrome。
接著是輸入 URL 后的「回車」,這時(shí)瀏覽器會(huì)對(duì) URL 進(jìn)行檢查,首先判斷協(xié)議,如果是 http 就按照 Web 來處理,另外還會(huì)對(duì)這個(gè) URL 進(jìn)行安全檢查,然后直接調(diào)用瀏覽器內(nèi)核中的對(duì)應(yīng)方法,比如WebView中的 loadUrl 方法。
在瀏覽器內(nèi)核中會(huì)先查看緩存,然后設(shè)置 UA 等 HTTP 信息,接著調(diào)用不同平臺(tái)下網(wǎng)絡(luò)請(qǐng)求的方法。
需要注意瀏覽器和瀏覽器內(nèi)核是不同的概念,瀏覽器指的是 Chrome、Firefox,而瀏覽器內(nèi)核則是 Blink、Gecko,瀏覽器內(nèi)核只負(fù)責(zé)渲染,GUI 及網(wǎng)絡(luò)連接等跨平臺(tái)工作則是瀏覽器實(shí)現(xiàn)的
HTTP 請(qǐng)求的發(fā)送
因?yàn)榫W(wǎng)絡(luò)的底層實(shí)現(xiàn)是和內(nèi)核相關(guān)的,所以這一部分需要針對(duì)不同平臺(tái)進(jìn)行處理,從應(yīng)用層角度看主要做兩件事情:通過 DNS 查詢 IP、通過 Socket 發(fā)送數(shù)據(jù),接下來就分別介紹這兩方面的內(nèi)容。
DNS 查詢
應(yīng)用程序可以直接調(diào)用 Libc 提供的getaddrinfo()方法來實(shí)現(xiàn) DNS 查詢。
DNS 查詢其實(shí)是基于 UDP 來實(shí)現(xiàn)的,這里我們通過一個(gè)具體例子來了解它的查找過程,以下是使用dig +trace fex.baidu.com命令得到的結(jié)果(省略了一些):
;<<>>DiG9.8.3-P1<<>>+trace fex.baidu.com;;globaloptions:+cmd. 11157 INNSg.root-servers.net.. 11157 INNSi.root-servers.net.. 11157 INNSj.root-servers.net.. 11157 INNSa.root-servers.net.. 11157 INNSl.root-servers.net.;;Received228bytes from8.8.8.8#53(8.8.8.8) in 220 mscom.172800INNSa.gtld-servers.net.com.172800INNSc.gtld-servers.net.com.172800INNSm.gtld-servers.net.com.172800INNSh.gtld-servers.net.com.172800INNSe.gtld-servers.net.;;Received503bytes from192.36.148.17#53(192.36.148.17) in 185 msbaidu.com.172800INNSdns.baidu.com.baidu.com.172800INNSns2.baidu.com.baidu.com.172800INNSns3.baidu.com.baidu.com.172800INNSns4.baidu.com.baidu.com.172800INNSns7.baidu.com.;;Received201bytes from192.48.79.30#53(192.48.79.30) in 1237 msfex.baidu.com.7200INCNAME fexteam.duapp.com.fexteam.duapp.com.300INCNAME duapp.n.shifen.com.n.shifen.com. 86400 INNSns1.n.shifen.com.n.shifen.com. 86400 INNSns4.n.shifen.com.n.shifen.com. 86400 INNSns2.n.shifen.com.n.shifen.com. 86400 INNSns5.n.shifen.com.n.shifen.com. 86400 INNSns3.n.shifen.com.;;Received258bytes from61.135.165.235#53(61.135.165.235) in 2 ms
可以看到這是一個(gè)逐步縮小范圍的查找過程,首先由本機(jī)所設(shè)置的 DNS 服務(wù)器(8.8.8.8)向 DNS 根節(jié)點(diǎn)查詢負(fù)責(zé) .com 區(qū)域的域務(wù)器,然后通過其中一個(gè)負(fù)責(zé) .com 的服務(wù)器查詢負(fù)責(zé) baidu.com 的服務(wù)器,最后由其中一個(gè) baidu.com 的域名服務(wù)器查詢 fex.baidu.com 域名的地址。
可能你在查詢某些域名的時(shí)會(huì)發(fā)現(xiàn)和上面不一樣,最底將看到有個(gè)奇怪的服務(wù)器搶先返回結(jié)果。。。
這里為了方便描述,忽略了很多不同的情況,比如 127.0.0.1 其實(shí)走的是loopback,和網(wǎng)卡設(shè)備沒關(guān)系;比如 Chrome 會(huì)在瀏覽器啟動(dòng)的時(shí)預(yù)先查詢 10 個(gè)你有可能訪問的域名;還有 Hosts 文件、緩存時(shí)間 TTL(Time to live)的影響等。
通過 Socket 發(fā)送數(shù)據(jù)
有了 IP 地址,就可以通過 Socket API 來發(fā)送數(shù)據(jù)了,這時(shí)可以選擇 TCP 或 UDP 協(xié)議,具體使用方法這里就不介紹了,推薦閱讀 Beej’s Guide to Network Programming。
HTTP 常用的是 TCP 協(xié)議,由于 TCP 協(xié)議的具體細(xì)節(jié)到處都能看到,所以本文就不介紹了,這里談一下 TCP 的 Head-of-line blocking 問題:假設(shè)客戶端的發(fā)送了 3 個(gè) TCP 片段(segments),編號(hào)分別是 1、2、3,如果編號(hào)為 1 的包傳輸時(shí)丟了,即便編號(hào) 2 和 3 已經(jīng)到達(dá)也只能等待,因?yàn)?TCP 協(xié)議需要保證順序,這個(gè)問題在 HTTP pipelining 下更嚴(yán)重,因?yàn)?HTTP pipelining 可以讓多個(gè) HTTP 請(qǐng)求通過一個(gè) TCP 發(fā)送,比如發(fā)送兩張圖片,可能第二張圖片的數(shù)據(jù)已經(jīng)全收到了,但還得等第一張圖片的數(shù)據(jù)傳到。
為了解決 TCP 協(xié)議的性能問題,Chrome 團(tuán)隊(duì)去年提出了QUIC協(xié)議,它是基于 UDP 實(shí)現(xiàn)的可靠傳輸,比起 TCP,它能減少很多來回(round trip)時(shí)間,還有前向糾錯(cuò)碼(Forward Error Correction)等功能。目前 Google Plus、 Gmail、Google Search、blogspot、Youtube 等幾乎大部分 Google 產(chǎn)品都在使用 QUIC,可以通過chrome://net-internals/#spdy頁(yè)面來發(fā)現(xiàn)。
雖然目前除了 Google 還沒人用 QUIC,但我覺得挺有前景的,因?yàn)閮?yōu)化 TCP 需要升級(jí)系統(tǒng)內(nèi)核(比如Fast Open)。
瀏覽器對(duì)同一個(gè)域名有連接數(shù)限制,大部分是 6,我以前認(rèn)為將這個(gè)連接數(shù)改大后會(huì)提升性能,但實(shí)際上并不是這樣的,Chrome 團(tuán)隊(duì)有做過實(shí)驗(yàn),發(fā)現(xiàn)從 6 改成 10 后性能反而下降了,造成這個(gè)現(xiàn)象的因素有很多,如建立連接的開銷、擁塞控制等問題,而像 SPDY、HTTP 2.0 協(xié)議盡管只使用一個(gè) TCP 連接來傳輸數(shù)據(jù),但性能反而更好,而且還能實(shí)現(xiàn)請(qǐng)求優(yōu)先級(jí)。
另外,因?yàn)?HTTP 請(qǐng)求是純文本格式的,所以在 TCP 的數(shù)據(jù)段中可以直接分析 HTTP 的文本,如果發(fā)現(xiàn)。。。
Socket 在內(nèi)核中的實(shí)現(xiàn)
前面說到瀏覽器的跨平臺(tái)庫(kù)通過調(diào)用 Socket API 來發(fā)送數(shù)據(jù),那么 Socket API 是如何實(shí)現(xiàn)的呢?
以 Linux 為例,它的實(shí)現(xiàn)在這里socket.c,目前我還不太了解,推薦讀者看看Linux kernel map,它標(biāo)注出了關(guān)鍵路徑的函數(shù),方便學(xué)習(xí)從協(xié)議棧到網(wǎng)卡驅(qū)動(dòng)的實(shí)現(xiàn)。
底層網(wǎng)絡(luò)協(xié)議的具體例子
接下來如果繼續(xù)介紹 IP 協(xié)議和 MAC 協(xié)議可能很多讀者會(huì)暈,所以本節(jié)將使用Wireshark來通過具體例子講解,以下是我請(qǐng)求百度首頁(yè)時(shí)抓取到的網(wǎng)絡(luò)數(shù)據(jù):
最底下是實(shí)際的二進(jìn)制數(shù)據(jù),中間是解析出來的各個(gè)字段值,可以看到其中最底部為 HTTP 協(xié)議(Hypertext Transfer Protocol),在 HTTP 之前有 54 字節(jié)(0×36),這就是底層網(wǎng)絡(luò)協(xié)議所帶來的開銷,我們接下來對(duì)這些協(xié)議進(jìn)行分析。
在 HTTP 之上是 TCP 協(xié)議(Transmission Control Protocol),它的具體內(nèi)容如下圖所示:
通過底部的二進(jìn)制數(shù)據(jù),可以看到 TCP 協(xié)議是加在 HTTP 文本前面的,它有 20 個(gè)字節(jié),其中定義了本地端口(Source port)和目標(biāo)端口(Destination port)、順序序號(hào)(Sequence Number)、窗口長(zhǎng)度等信息,以下是 TCP 協(xié)議各個(gè)部分?jǐn)?shù)據(jù)的完整介紹:
0 1 2 3
01234567890123456789012345678901+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Source Port| Destination Port|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Sequence Number|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Acknowledgment Number|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Data| |U|A|E|R|S|F| ||Offset|Reserved|R|C|O|S|Y|I|Window || | |G|K|L|T|N|N| |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Checksum| Urgent Pointer|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Options|Padding|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| data|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
具體各個(gè)字段的作用這里就不介紹了,感興趣的讀者可以閱讀RFC 793,并結(jié)合抓包分析來理解。
需要注意的是,在 TCP 協(xié)議中并沒有 IP 地址信息,因?yàn)檫@是在上一層的 IP 協(xié)議中定義的,如下圖所示:
IP 協(xié)議同樣是在 TCP 前面的,它也有 20 字節(jié),在這里指明了版本號(hào)(Version)為 4,源(Source) IP 為192.168.1.106,目標(biāo)(Destination) IP 為119.75.217.56,因此 IP 協(xié)議最重要的作用就是確定 IP 地址。
因?yàn)?IP 協(xié)議中可以查看到目標(biāo) IP 地址,所以如果發(fā)現(xiàn)某些特定的 IP 地址,某些路由器就會(huì)。。。
但是,光靠 IP 地址是無(wú)法進(jìn)行通信的,因?yàn)?IP 地址并不和某臺(tái)設(shè)備綁定,比如你的筆記本的 IP 在家中是192.168.1.1,但到公司就變成172.22.22.22了,所以在底層通信時(shí)需要使用一個(gè)固定的地址,這就是 MAC(media Access control) 地址,每個(gè)網(wǎng)卡出廠時(shí)的 MAC 地址都是固定且唯一的。
因此再往上就是 MAC 協(xié)議,它有 14 字節(jié),如下所示:
當(dāng)一臺(tái)電腦加入網(wǎng)絡(luò)時(shí),需要通過ARP協(xié)議告訴其它網(wǎng)絡(luò)設(shè)備它的 IP 及對(duì)應(yīng)的 MAC 地址是什么,這樣其它設(shè)備就能通過 IP 地址來查找對(duì)應(yīng)的設(shè)備了。
最頂上的 Frame 是代表 Wireshark 的抓包序號(hào),并不是網(wǎng)絡(luò)協(xié)議。
就這樣,我們解答了第二個(gè)問題,不過其實(shí)這里面還有很多很多細(xì)節(jié)沒介紹。
第三個(gè)問題:數(shù)據(jù)如何從本機(jī)網(wǎng)卡發(fā)送到服務(wù)器?
從內(nèi)核到網(wǎng)絡(luò)適配器(Network Interface Card)
前面說到調(diào)用 Socket API 后內(nèi)核會(huì)對(duì)數(shù)據(jù)進(jìn)行底層協(xié)議棧的封裝,接下來啟動(dòng)DMA控制器,它將從內(nèi)存中讀取數(shù)據(jù)寫入網(wǎng)卡。
以 Nexus 5 為例,它使用的是博通BCM4339芯片通信,接口采用了 SD 卡一樣的SDIO,但這個(gè)芯片的細(xì)節(jié)并沒有公開資料,所以這里就不討論了。
連接 Wi-Fi 路由
Wi-Fi 網(wǎng)卡需要通過 Wi-Fi 路由來與外部通信,原理是基于無(wú)線電,通過電流變化來產(chǎn)生無(wú)線電,這個(gè)過程也叫「調(diào)制」,而反過來無(wú)線電可以引起電磁場(chǎng)變化,從而產(chǎn)生電流變化,利用這個(gè)原理就能將無(wú)線電中的信息解讀出來就叫「解調(diào)」,其中單位時(shí)間內(nèi)變化的次數(shù)就稱為頻率,目前在 Wi-Fi 中所采用的頻率分為 2.4 GHz 和 5 GHz 兩種。
在同一個(gè) Wi-Fi 路由下,因?yàn)椴捎玫念l率相同,同時(shí)使用時(shí)會(huì)發(fā)生沖突,為了解決這個(gè)問題,Wi-Fi 采用了被稱為CSMA/CA的方法,簡(jiǎn)單來說就是在傳輸前先確認(rèn)信道是否已被使用,沒有才發(fā)送數(shù)據(jù)。
而同樣基于無(wú)線電原理的 2G/3G/LTE 也會(huì)遇到類似的問題,但它并沒有采用 Wi-Fi 那樣的獨(dú)占方案,而是通過頻分(FDMA)、時(shí)分(TDMA)和碼分(CDMA)來進(jìn)行復(fù)用,具體細(xì)節(jié)這里就不展開了。
以小米路由為例,它使用的芯片是BCM 4709,這個(gè)芯片由 ARM Cortex-A9 處理器及流量(Flow)硬件加速組成,使用硬件芯片可以避免經(jīng)過操作系統(tǒng)中斷、上下文切換等操作,從而提升了性能。
路由器中的操作系統(tǒng)可以基于OpenWrt或DD-WRT來開發(fā)的,具體細(xì)節(jié)我不太了解,所以就不展開了。
因?yàn)閮?nèi)網(wǎng)設(shè)備的 IP 都是類似192.168.1.x這樣的內(nèi)網(wǎng)地址,外網(wǎng)無(wú)法直接向這個(gè)地址發(fā)送數(shù)據(jù),所以網(wǎng)絡(luò)數(shù)據(jù)在經(jīng)過路由時(shí),路由會(huì)修改相關(guān)地址和端口,這個(gè)操作稱為NAT映射。
最后家庭路由一般會(huì)通過雙絞線連接到運(yùn)營(yíng)商網(wǎng)絡(luò)的。
運(yùn)營(yíng)商網(wǎng)絡(luò)內(nèi)的路由
數(shù)據(jù)過雙絞線發(fā)送到運(yùn)營(yíng)商網(wǎng)絡(luò)后,還會(huì)經(jīng)過很多個(gè)中間路由轉(zhuǎn)發(fā),讀者可以通過 traceroute 命令或者在線可視化工具來查看這些路由的 ip 和位置。
當(dāng)數(shù)據(jù)傳遞到這些路由器后,路由器會(huì)取出包中目的地址的前綴,通過內(nèi)部的轉(zhuǎn)發(fā)表查找對(duì)應(yīng)的輸出鏈路,而這個(gè)轉(zhuǎn)發(fā)表是如何得到的呢?這就是路由器中最重要的選路算法了,可選的有很多,我對(duì)這方面并不太了解,看起來維基百科上的詞條列得很全。
主干網(wǎng)間的傳輸
對(duì)于長(zhǎng)線的數(shù)據(jù)傳輸,通常使用光纖作為介質(zhì),光纖是基于光的全反射來實(shí)現(xiàn)的,使用光纖需要專門的發(fā)射器通過電致發(fā)光(比如 LED)將電信號(hào)轉(zhuǎn)成光,比起前面介紹的無(wú)線電和雙絞線,光纖信號(hào)的抗干擾性要強(qiáng)得多,而且能耗也小很多。
既然是基于光來傳輸數(shù)據(jù),數(shù)據(jù)傳輸速度也就取決于光的速度,在真空中的光速接近于 30 萬(wàn)千米/秒,由于光纖包層(cladding)中的折射率(refractive index)為 1.52,所以實(shí)際光速是 20 萬(wàn)千米/秒左右,從首都機(jī)場(chǎng)飛往廣州白云機(jī)場(chǎng)的距離是 1967 千米,按照這個(gè)距離來算需要花費(fèi) 10 毫秒才能抵達(dá)。這意味著如果你在北京,服務(wù)器在廣州,等你發(fā)出數(shù)據(jù)到服務(wù)器返回?cái)?shù)據(jù)至少得等 20 毫秒,實(shí)際情況預(yù)計(jì)是 2- 3 倍,因?yàn)檫@其中還有各個(gè)節(jié)點(diǎn)路由處理的耗時(shí),比如我測(cè)試了一個(gè)廣州的 IP 發(fā)現(xiàn)平均延遲為 60 毫秒。
這個(gè)延遲是現(xiàn)有科技無(wú)法解決的(除非找到超過光速的方法),只能通過 CDN 來讓傳輸距離變短,或盡量減少串行的來回請(qǐng)求(比如 TCP 建立連接所需的 3 次握手)。