主頁 > 後端開發 > 在瀏覽器輸入 URL 回車后,會發生什么?

在瀏覽器輸入 URL 回車后,會發生什么?

2021-01-16 06:38:57 後端開發

作者:4Ark
來源:https://4ark.me/post/b6c7c0a2.html

這個問題已經是老生常談了,更是經常被作為面試的壓軸題出現,網上也有很多文章,但最近閑的無聊,然后就自己做了一篇筆記,感覺比之前理解更透徹了,

注意:本文的步驟是建立在,請求的是一個簡單的 HTTP 請求,沒有 HTTPS、HTTP2、最簡單的 DNS、沒有代理、并且服務器沒有任何問題的基礎上,盡管這是不切實際的,

大致流程

  • URL 決議

  • DNS 查詢

  • TCP 連接

  • 處理請求

  • 接受回應

  • 渲染頁面

一、URL 決議

地址決議:

首先判斷你輸入的是一個合法的 URL 還是一個待搜索的關鍵詞,并且根據你輸入的內容進行自動完成、字符編碼等操作,

HSTS

由于安全隱患,會使用 HSTS 強制客戶端使用 HTTPS 訪問頁面

其他操作

瀏覽器還會進行一些額外的操作,比如安全檢查、訪問限制(之前國產瀏覽器限制 996.icu),

檢查快取

二、DNS 查詢

基本步驟

1. 瀏覽器快取

瀏覽器會先檢查是否在快取中,沒有則呼叫系統庫函式進行查詢,

2. 作業系統快取

作業系統也有自己的 DNS快取,但在這之前,會向檢查域名是否存在本地的 Hosts 檔案里,沒有則向 DNS 服務器發送查詢請求,

3. 路由器快取

路由器也有自己的快取,

4. ISP DNS 快取

ISP DNS 就是在客戶端電腦上設定的首選 DNS 服務器,它們在大多數情況下都會有快取,

根域名服務器查詢

在前面所有步驟沒有快取的情況下,本地 DNS 服務器會將請求轉發到互聯網上的根域,下面這個圖很好的詮釋了整個流程:

根域名服務器(維基百科)

需要注意的點

遞回方式:一路查下去中間不回傳,得到最終結果才回傳資訊(瀏覽器到本地DNS服務器的程序)

迭代方式,就是本地DNS服務器到根域名服務器查詢的方式,

什么是 DNS 劫持

前端 dns-prefetch 優化

三、TCP 連接

TCP/IP 分為四層,在發送資料時,每層都要對資料進行封裝:

1. 應用層:發送 HTTP 請求

在前面的步驟我們已經得到服務器的 IP 地址,瀏覽器會開始構造一個 HTTP 報文,其中包括:

  • 請求報頭(Request Header):請求方法、目標地址、遵循的協議等等

  • 請求主體(其他引數)

其中需要注意的點:瀏覽器只能發送 GET、POST 方法,而打開網頁使用的是 GET 方法

2. 傳輸層:TCP 傳輸報文

傳輸層會發起一條到達服務器的 TCP 連接,為了方便傳輸,會對資料進行分割(以報文段為單位),并標記編號,方便服務器接受時能夠準確地還原報文資訊,

在建立連接前,會先進行 TCP 三次握手,

關于 TCP/IP 三次握手,網上已經有很多段子和圖片生動地描述了,大家可以看下這篇:為什么 TCP 建立連接是三次握手,關閉連接確是四次揮手呢?

3. 網路層:IP協議查詢Mac地址

將資料段打包,并加入源及目標的IP地址,并且負責尋找傳輸路線,

判斷目標地址是否與當前地址處于同一網路中,是的話直接根據 Mac 地址發送,否則使用路由表查找下一跳地址,以及使用 ARP 協議查詢它的 Mac 地址,

注意:在 OSI 參考模型中 ARP 協議位于鏈路層,但在 TCP/IP 中,它位于網路層,

4. 鏈路層:以太網協議

以太網協議

根據以太網協議將資料分為以“幀”為單位的資料包,每一幀分為兩個部分:

  • 標頭:資料包的發送者、接受者、資料型別

  • 資料:資料包具體內容

Mac 地址

以太網規定了連入網路的所有設備都必須具備“網卡”介面,資料包都是從一塊網卡傳遞到另一塊網卡,網卡的地址就是 Mac 地址,每一個 Mac 地址都是獨一無二的,具備了一對一的能力,

廣播

發送資料的方法很原始,直接把資料通過 ARP 協議,向本網路的所有機器發送,接收方根據標頭資訊與自身 Mac 地址比較,一致就接受,否則丟棄,

注意:接收方回應是單播,

服務器接受請求

接受程序就是把以上步驟逆轉過來,參見上圖,

四、服務器處理請求

大致流程

HTTPD

最常見的 HTTPD 有 Linux 上常用的 Apache 和 Nginx,以及 Windows 上的 IIS,

它會監聽得到的請求,然后開啟一個子行程去處理這個請求,

處理請求

接受 TCP 報文后,會對連接進行處理,對HTTP協議進行決議(請求方法、域名、路徑等),并且進行一些驗證:

  • 驗證是否配置虛擬主機

  • 驗證虛擬主機是否接受此方法

  • 驗證該用戶可以使用該方法(根據 IP 地址、身份資訊等)

重定向

假如服務器配置了 HTTP 重定向,就會回傳一個 301永久重定向回應,瀏覽器就會根據回應,重新發送 HTTP 請求(重新執行上面的程序),

URL 重寫

然后會查看 URL 重寫規則,如果請求的檔案是真實存在的,比如圖片、html、css、js檔案等,則會直接把這個檔案回傳,否則服務器會按照規則把請求重寫到 一個 REST 風格的 URL 上,然后根據動態語言的腳本,來決定呼叫什么型別的動態檔案解釋器來處理這個請求,

以 PHP 語言的 MVC 框架舉例,它首先會初始化一些環境的引數,根據 URL 由上到下地去匹配路由,然后讓路由所定義的方法去處理請求,

關注微信公眾號:Java技術堆疊,在后臺回復:架構,可以獲取我整理的 N 篇最新架構教程,都是干貨,

五、瀏覽器接受回應

瀏覽器接收到來自服務器的回應資源后,會對資源進行分析,

首先查看 Response header,根據不同狀態碼做不同的事(比如上面提到的重定向),

如果回應資源進行了壓縮(比如 gzip),還需要進行解壓,

然后,對回應資源做快取,

接下來,根據回應資源里的 MIME[3] 型別去決議回應內容(比如 HTML、Image各有不同的決議方式),

六、渲染頁面

瀏覽器內核

不同的瀏覽器內核,渲染程序也不完全相同,但大致流程都差不多,

基本流程

6.1. HTML 決議

首先要知道瀏覽器決議是從上往下一行一行地決議的,

決議的程序可以分為四個步驟:

① 解碼(encoding)

傳輸回來的其實都是一些二進制位元組資料,瀏覽器需要根據檔案指定編碼(例如UTF-8)轉換成字串,也就是HTML 代碼,

② 預決議(pre-parsing)

預決議做的事情是提前加載資源,減少處理時間,它會識別一些會請求資源的屬性,比如img標簽的src屬性,并將這個請求加到請求佇列中,

③ 符號化(Tokenization)

符號化是詞法分析的程序,將輸入決議成符號,HTML 符號包括,開始標簽、結束標簽、屬性名和屬性值,

它通過一個狀態機去識別符號的狀態,比如遇到<>狀態都會產生變化,

④ 構建樹(tree construction)

注意:符號化和構建樹是并行操作的,也就是說只要決議到一個開始標簽,就會創建一個 DOM 節點,

在上一步符號化中,決議器獲得這些標記,然后以合適的方法創建DOM物件并把這些符號插入到DOM物件中,

<html><head><title>Web page parsing</title></head><body><div><h1>Web page parsing</h1><p>This is an example Web page.</p></div></body></html>

瀏覽器容錯進制

你從來沒有在瀏覽器看過類似”語法無效”的錯誤,這是因為瀏覽器去糾正錯誤的語法,然后繼續作業,

事件

當整個決議的程序完成以后,瀏覽器會通過DOMContentLoaded事件來通知DOM決議完成,

6.2. CSS 決議

一旦瀏覽器下載了 CSS,CSS 決議器就會處理它遇到的任何 CSS,根據語法規范[4]決議出所有的 CSS 并進行標記化,然后我們得到一個規則表,

CSS 匹配規則

在匹配一個節點對應的 CSS 規則時,是按照從右到左的順序的,例如:div p { font-size :14px }會先尋找所有的p標簽然后判斷它的父元素是否為div

所以我們寫 CSS 時,盡量用 id 和 class,千萬不要過度層疊,

6.3. 渲染樹

其實這就是一個 DOM 樹和 CSS 規則樹合并的程序,

注意:渲染樹會忽略那些不需要渲染的節點,比如設定了display:none的節點,

計算

通過計算讓任何尺寸值都減少到三個可能之一:auto、百分比、px,比如把rem轉化為px

級聯

瀏覽器需要一種方法來確定哪些樣式才真正需要應用到對應元素,所以它使用一個叫做specificity的公式,這個公式會通過:

  • 標簽名、class、id

  • 是否行內樣式

  • !important

然后得出一個權重值,取最高的那個,

渲染阻塞

當遇到一個script標簽時,DOM 構建會被暫停,直至腳本完成執行,然后繼續構建 DOM 樹,

但如果 JS 依賴 CSS 樣式,而它還沒有被下載和構建時,瀏覽器就會延遲腳本執行,直至 CSS Rules 被構建,

所有我們知道:

  • CSS 會阻塞 JS 執行

  • JS 會阻塞后面的 DOM 決議

為了避免這種情況,應該以下原則:

  • CSS 資源排在 JavaScript 資源前面

  • JS 放在 HTML 最底部,也就是 </body>

另外,如果要改變阻塞模式,可以使用 defer 與 async,

6.4. 布局與繪制

確定渲染樹種所有節點的幾何屬性,比如:位置、大小等等,最后輸入一個盒子模型,它能精準地捕獲到每個元素在螢屏內的準確位置與大小,

然后遍歷渲染樹,呼叫渲染器的 paint() 方法在螢屏上顯示其內容,

6.5. 合并渲染層

把以上繪制的所有圖片合并,最終輸出一張圖片,

6.6. 回流與重繪

回流(reflow)

當瀏覽器發現某個部分發現變化影響了布局時,需要倒回去重新渲染,會從html標簽開始遞回往下,重新計算位置和大小,

reflow基本是無法避免的,因為當你滑動一下滑鼠、resize 視窗,頁面就會產生變化,

重繪(repaint)

改變了某個元素的背景色、文字顏色等等不會影響周圍元素的位置變化時,就會發生重繪,

每次重繪后,瀏覽器還需要合并渲染層并輸出到螢屏上,

回流的成本要比重繪高很多,所以我們應該盡量避免產生回流,

比如:display:none 會觸發回流,而 visibility:hidden 只會觸發重繪,

6.7. JavaScript 編譯執行

大致流程

可以分為三個階段:

1. 詞法分析

JS 腳本加載完畢后,會首先進入語法分析階段,它首先會分析代碼塊的語法是否正確,不正確則拋出“語法錯誤”,停止執行,

幾個步驟:

  • 分詞,例如將var a = 2,,分成vara=2這樣的詞法單元,

  • 決議,將詞法單元轉換成抽象語法樹(AST),

  • 代碼生成,將抽象語法樹轉換成機器指令,

2. 預編譯

JS 有三種運行環境:

  • 全域環境

  • 函式環境

  • eval

每進入一個不同的運行環境都會創建一個對應的執行背景關系,根據不同的背景關系環境,形成一個函式呼叫堆疊,堆疊底永遠是全域執行背景關系,堆疊頂則永遠是當前執行背景關系,

創建執行背景關系

創建執行背景關系的程序中,主要做了以下三件事:

  • 創建變數物件

  • 引數、函式、變數

  • 建立作用域鏈

  • 確認當前執行環境是否能訪問變數

  • 確定 This 指向

3. 執行

JS 執行緒

雖然 JS 是單執行緒的,但實際上參與作業的執行緒一共有四個:

其中三個只是協助,只有 JS 引擎執行緒是真正執行的

其中三個只是協助,只有 JS 引擎執行緒是真正執行的

JS 引擎執行緒:也叫 JS 內核,負責決議執行 JS 腳本程式的主執行緒,例如 V8 引擎事件觸發執行緒:屬于瀏覽器內核執行緒,主要用于控制事件,例如滑鼠、鍵盤等,當事件被觸發時,就會把事件的處理函式推進事件佇列,等待 JS 引擎執行緒執行定時器觸發執行緒:主要控制setIntervalsetTimeout,用來計時,計時完畢后,則把定時器的處理函式推進事件佇列中,等待 JS 引擎執行緒,HTTP 異步請求執行緒:通過XMLHttpRequest連接后,通過瀏覽器新開的一個執行緒,監控readyState狀態變更時,如果設定了該狀態的回呼函式,則將該狀態的處理函式推進事件佇列中,等待JS引擎執行緒執行,

注:瀏覽器對同一域名的并發連接數是有限的,通常為 6 個,

宏任務

分為:

  • 同步任務:按照順序執行,只有前一個任務完成后,才能執行后一個任務

  • 異步任務:不直接執行,只有滿足觸發條件時,相關的執行緒將該異步任務推進任務佇列中,等待JS引擎主執行緒上的任務執行完畢時才開始執行,例如異步Ajax、DOM事件,setTimeout等,

微任務

微任務是ES6和Node環境下的,主要 API 有:Promiseprocess.nextTick

微任務的執行在宏任務的同步任務之后,在異步任務之前,

代碼例子

console.log('1'); // 宏任務 同步
setTimeout(function() {
	console.log('2'); 	// 宏任務 異步})
new Promise(function(resolve) {
	console.log('3'); 	// 宏任務 同步    
	resolve();
}).then(function() {
	console.log('4')// 微任務})
	console.log('5') // 宏任務 同步

以上代碼輸出順序為:1,3,5,4,2

近期熱文推薦:

1.Java 15 正式發布, 14 個新特性,重繪你的認知!!

2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!

3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,

4.吊打 Tomcat ,Undertow 性能很炸!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/249313.html

標籤:Java

上一篇:java爬蟲-初識

下一篇:2021金三銀四必備JVM知識點與JVM面試題分享

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more