鍵入網址再按下回車,後面究竟發生了什麼?
- 使用IP位址通路Web伺服器
- 抓包分析
- 使用域名通路Web伺服器
- 真實的網絡世界
- 小結
- 課下作業
使用IP位址通路Web伺服器
首先我們運作www目錄下的“start”批處理程式,啟動本機的OpenResty伺服器,啟動後可以用“list”批處理确認服務是否正常運作。
然後我們打開Wireshark,選擇“HTTP TCP port(80)”過濾器,再滑鼠輕按兩下“Npcap loopback Adapter”,開始抓取本機127.0.0.1位址上的網絡資料。
第三步,在Chrome浏覽器的位址欄裡輸入“http://127.0.0.1/”,再按下Enter鍵,等歡迎頁面顯示出來後Wireshark裡就會有捕獲的資料包,如下圖所示。

如果你還沒有搭好實驗環境,或者捕獲與本文裡的不一緻也沒關系。我把這次捕獲的資料存成了pcap包,檔案名是“08-1”,放到了GitHub上,你可以下載下傳到本地後再用Wireshark打開,完全精确“重放”剛才的HTTP傳輸過程。
抓包分析
在Wireshark裡你可以看到,這次一共抓到了11個包(這裡用了濾包功能,濾掉了3個包,原本是14個包),耗時0.65秒,下面我們就來一起分析一下"鍵入網址按下回車"後資料傳輸的全過程。
通過前面“破冰篇”的講解,你應該知道HTTP協定是運作在TCP/IP基礎上的,依靠TCP/IP協定來實作資料的可靠傳輸。是以浏覽器要用HTTP協定收發資料,首先要做的就是建立TCP連接配接。
因為我們在位址欄裡直接輸入了IP位址“127.0.0.1”,而Web伺服器的預設端口是80,是以浏覽器就要依照TCP協定的規範,使用“三次握手”建立與Web伺服器的連接配接。
對應到Wireshark裡,就是最開始的三個抓包,浏覽器使用的端口是52085,伺服器使用的端口是80,經過SYN、SYN/ACK、ACK的三個包之後,浏覽器與伺服器的TCP連接配接就建立起來了。
有了可靠的TCP連接配接通道後,HTTP協定就可以開始工作了。于是,浏覽器按照HTTP協定規定的格式,通過TCP發送了一個“GET / HTTP/1.1”請求封包,也就是Wireshark裡的第四個包。至于包的内容具體是什麼現在先不用管,我們下一講再說。
随後,Web伺服器回複了第五個包,在TCP協定層面确認:“剛才的封包我已經收到了”,不過這個TCP包HTTP協定是看不見的。
Web伺服器收到封包後在内部就要處理這個請求。同樣也是依據HTTP協定的規定,解析封包,看看浏覽器發送這個請求想要幹什麼。
它一看,原來是要求擷取根目錄下的預設檔案,好吧,那我就從磁盤上把那個檔案全讀出來,再拼成符合HTTP格式的封包,發回去吧。這就是Wireshark裡的第六個包“HTTP/1.1 200 OK”,底層走的還是TCP協定。
同樣的,浏覽器也要給伺服器回複一個TCP的ACK确認,“你的響應封包收到了,多謝”,即第七個包。
這時浏覽器就收到了響應資料,但裡面是什麼呢?是以也要解析封包。一看,伺服器給我的是個HTML檔案,好,那我就調用排版引擎、JavaScript引擎等等處理一下,然後在浏覽器視窗裡展現出了歡迎頁面。
這之後還有兩個來回,共四個包,重複了相同的步驟。這是浏覽器自動請求了作為網站圖示的“favicon.ico”檔案,與我們輸入的網址無關。但因為我們的實驗環境沒有這個檔案,是以伺服器在硬碟上找不到,傳回了一個“404 Not Found”。
至此,“鍵入網址再按下回車”的全過程就結束了。
我為這個過程畫了一個互動圖,你可以對照着看一下。不過要提醒你,圖裡TCP關閉連接配接的“四次揮手”在抓包裡沒有出現,這是因為HTTP/1.1長連接配接特性,預設不會立即關閉連接配接。
再簡要叙述一下這次最簡單的浏覽器HTTP請求過程:
- 浏覽器從位址欄的輸入中獲得伺服器的IP位址和端口号;
- 浏覽器用TCP的三次握手與伺服器建立連接配接;
- 浏覽器向伺服器發送拼好的封包;
- 伺服器收到封包後處理請求,同樣拼好封包再發給浏覽器;
- 浏覽器解析封包,渲染輸出頁面。
使用域名通路Web伺服器
剛才我們是在浏覽器位址欄裡直接輸入IP位址,但絕大多數情況下,我們是不知道伺服器IP位址的,使用的是域名,那麼改用域名後這個過程會有什麼不同嗎?
還是實際動手試一下吧,把位址欄的輸入改成“http://www.chrono.com”,重複Wireshark抓包過程,你會發現,好像沒有什麼不同,浏覽器上同樣顯示出了歡迎界面,抓到的包也同樣是11個:先是三次握手,然後是兩次HTTP傳輸。
這裡就出現了一個問題:浏覽器是如何從網址裡知道“www.chrono.com”的IP位址就是“127.0.0.1”的呢?
還記得我們之前講過的DNS知識嗎?浏覽器看到了網址裡的“www.chrono.com”,發現它不是數字形式的IP位址,那就肯定是域名了,于是就會發起域名解析動作,通過通路一系列的域名解析伺服器,試圖把這個域名翻譯成TCP/IP協定裡的IP位址。
不過因為域名解析的全過程實在是太複雜了,如果每一個域名都要大費周折地去網上查一下,那我們上網肯定會慢得受不了。
是以,在域名解析的過程中會有多級的緩存,浏覽器首先看一下自己的緩存裡有沒有,如果沒有就向作業系統的緩存要,還沒有就檢查本機域名解析檔案hosts,也就是上一講中我們修改的“C:WINDOWSsystem32driversetchosts”。
剛好,裡面有一行映射關系“127.0.0.1 www.chrono.com”,于是浏覽器就知道了域名對應的IP位址,就可以愉快地建立TCP連接配接發送HTTP請求了。
我把這個過程也畫出了一張圖,但省略了TCP/IP協定的互動部分,裡面的浏覽器多出了一個通路hosts檔案的動作,也就是本機的DNS解析。
真實的網絡世界
通過上面兩個在“最小化”環境裡的實驗,你是否已經對HTTP協定的工作流程有了基本的認識呢?
第一個實驗是最簡單的場景,隻有兩個角色:浏覽器和伺服器,浏覽器可以直接用IP位址找到伺服器,兩者直接建立TCP連接配接後發送HTTP封包通信。
第二個實驗在浏覽器和伺服器之外增加了一個DNS的角色,浏覽器不知道伺服器的IP位址,是以必須要借助DNS的域名解析功能得到伺服器的IP位址,然後才能與伺服器通信。
真實的網際網路世界要比這兩個場景要複雜的多,我利用下面的這張圖來做一個詳細的說明。
如果你用的是電腦桌上型電腦,那麼你可能會使用帶水晶頭的雙絞線連上網口,由交換機接入固定網絡。如果你用的是手機、平闆電腦,那麼你可能會通過蜂窩網絡、WiFi,由電信基站、無線熱點接入移動網絡。
接入網絡的同時,網絡運作商會給你的裝置配置設定一個IP位址,這個位址可能是靜态配置設定的,也可能是動态配置設定的。靜态IP就始終不變,而動态IP可能你下次上網就變了。
假設你要通路的是Apple網站,顯然你是不知道它的真實IP位址的,在浏覽器裡隻能使用域名“www.apple.com”通路,那麼接下來要做的必然是域名解析。這就要用DNS協定開始從作業系統、本地DNS、根DNS、頂級DNS、權威DNS的層層解析,當然這中間有緩存,可能不會費太多時間就能拿到結果。
别忘了網際網路上還有另外一個重要的角色CDN,它也會在DNS的解析過程中“插上一腳”。DNS解析可能會給出CDN伺服器的IP位址,這樣你拿到的就會是CDN伺服器而不是目标網站的實際位址。
因為CDN會緩存網站的大部分資源,比如圖檔、CSS樣式表,是以有的HTTP請求就不需要再發到Apple,CDN就可以直接響應你的請求,把資料發給你。
由PHP、Java等背景服務動态生成的頁面屬于“動态資源”,CDN無法緩存,隻能從目标網站擷取。于是你發出的HTTP請求就要開始在網際網路上的“漫長跋涉”,經過無數的路由器、網關、代理,最後到達目的地。
目标網站的伺服器對外表現的是一個IP位址,但為了能夠扛住高并發,在内部也是一套複雜的架構。通常在入口是負載均衡裝置,例如四層的LVS或者七層的Nginx,在後面是許多的伺服器,構成一個更強更穩定的叢集。
負載均衡裝置會先通路系統裡的緩存伺服器,通常有memory級緩存Redis和disk級緩存Varnish,它們的作用與CDN類似,不過是工作在内部網絡裡,把最頻繁通路的資料緩存幾秒鐘或幾分鐘,減輕後端應用伺服器的壓力。
如果緩存伺服器裡也沒有,那麼負載均衡裝置就要把請求轉發給應用伺服器了。這裡就是各種開發架構大顯神通的地方了,例如Java的Tomcat/Netty/Jetty,Python的Django,還有PHP、Node.js、Golang等等。它們又會再通路後面的MySQL、PostgreSQL、MongoDB等資料庫服務,實作使用者登入、商品查詢、購物下單、扣款支付等業務操作,然後把執行的結果傳回給負載均衡裝置,同時也可能給緩存伺服器裡也放一份。
應用伺服器的輸出到了負載均衡裝置這裡,請求的處理就算是完成了,就要按照原路再走回去,還是要經過許多的路由器、網關、代理。如果這個資源允許緩存,那麼經過CDN的時候它也會做緩存,這樣下次同樣的請求就不會到達源站了。
最後網站的響應資料回到了你的裝置,它可能是HTML、JSON、圖檔或者其他格式的資料,需要由浏覽器解析處理才能顯示出來,如果資料裡面還有超連結,指向别的資源,那麼就又要重走一遍整個流程,直到所有的資源都下載下傳完。
小結
今天我們在本機的環境裡做了兩個簡單的實驗,學習了HTTP協定請求-應答的全過程,在這裡做一個小結。
- HTTP協定基于底層的TCP/IP協定,是以必須要用IP位址建立連接配接;
- 如果不知道IP位址,就要用DNS協定去解析得到IP位址,否則就會連接配接失敗;
- 建立TCP連接配接後會順序收發資料,請求方和應答方都必須依據HTTP規範建構和解析封包;
- 為了減少響應時間,整個過程中的每一個環節都會有緩存,能夠實作“短路”操作;
- 雖然現實中的HTTP傳輸過程非常複雜,但理論上仍然可以簡化成實驗裡的“兩點”模型。
課下作業
- 你能試着解釋一下在浏覽器裡點選頁面連結後發生了哪些事情嗎?
- 浏覽器判斷這個連結是要在目前頁面打開還是新開标簽頁,然後走一遍本文中的通路過程:拿到ip位址和端口号,建立tcp/ip連結,發送請求封包,接收伺服器傳回并渲染
- 這一節課裡講的都是正常的請求處理流程,如果是一個不存在的域名,那麼浏覽器的工作流程會是怎麼樣的呢?
- 我們在上面已經大緻介紹了一下,點選頁面連接配接後,浏覽器判斷是不是ip位址,不是就進行域名解析,首先進入浏覽器緩存查找是否有過查詢記錄,沒有進入作業系統緩存,沒有再進入hosts緩存,如果還沒有就進入區域網路域名伺服器,接着是廣域網域名伺服器,再就是頂級域名伺服器,最後是根域名伺服器,如果都沒有,那就傳回錯誤頁面。