在咱們點擊一個網址,到它可以呈如今瀏覽器中,展現在咱們面前,這個過程當中,電腦裏,網絡上,究竟發生了什麼事情。php
那咱們就開始了,故事其實並非從在瀏覽器的地址欄輸入一個網址,或者咱們抓着鼠標點擊一個連接開始,事情的開端要追溯到服務器啓動監聽服務的時候,在某個未知的時刻,一臺機房裏普普統統的刀片服務器,加上電,啓動了操做系統,隨着操做系統的就緒,服務器啓動了 http 服務進程,這個 http 服務的守護進程,(daemon),多是 apache,也多是 nginx,無論怎麼說,這個 http 服務進程開始定位到服務器上的 www 文件夾,通常是位於 /var/www ,而後啓動了一些附屬的模塊,例如 php,或者,使用 fastcgi 方式鏈接到 php 的 fpm 管理進程,而後,向操做系統申請了一個 tcp 鏈接,而後綁定在了 80 端口,調用了 accept 函數,開始了默默的監聽,監聽着可能來自位於地球任何一個地方的請求,隨時準備作出響應。css
這個時候,典型的狀況下,機房裏面應該還有一個數據庫服務器,或許,還有一臺緩存服務器,若是對於流量巨大的網站,那麼動態腳本的解釋器可能還有單獨的物理機器來跑,若是是中小的站點,那麼,上述的各色服務,甚至均可能在一臺物理機上,這些服務監聽之間的關係。無論怎麼說,他們作好了準備,靜候差遣。html
當協議或主機名不合法時,瀏覽器會將地址欄中輸入的文字傳給默認的搜索引擎。大部分狀況下,在把文字傳遞給搜索引擎的時候,URL會帶有特定的一串字符,用來告訴搜索引擎此次搜索來自這個特定瀏覽器。mysql
這裏主機名是 google.com ,因此沒有非ASCII的字符;若是有的話,瀏覽器會對主機名部分使用 Punycode 編碼nginx
瀏覽器檢查自帶的「預加載 HSTS(HTTP嚴格傳輸安全)」列表,這個列表裏包含了那些請求瀏覽器只使用HTTPS進行鏈接的網站算法
要想發送 ARP(地址解析協議)廣播,咱們須要有一個目標 IP 地址,同時還須要知道用於發送 ARP 廣播的接口的 MAC 地址。sql
首先查詢 ARP 緩存,若是緩存命中,咱們返回結果:目標 IP = MAC
若是緩存沒有命中:chrome
查看路由表,看看目標 IP 地址是否是在本地路由表中的某個子網內。是的話,使用跟那個子網相連的接口,不然使用與默認網關相連的接口。數據庫
咱們發送一個二層( OSI 模型 中的數據鏈路層)ARP 請求:apache
1
2
3
4
|
Sender MAC: interface:mac:address:here
Sender IP: interface.ip.goes.here
Target MAC: FF:FF:FF:FF:FF:FF (Broadcast)
Target IP: target.ip.goes.here
|
根據鏈接主機和路由器的硬件類型不一樣,能夠分爲如下幾種狀況:
若是咱們鏈接到一個集線器,集線器會把 ARP 請求向全部其它端口廣播,若是路由器也「鏈接」在其中,它會返回一個 ARP Reply 。
交換機:
若是咱們鏈接到了一個交換機,交換機會檢查本地 CAM/MAC 表,看看哪一個端口有咱們要找的那個 MAC 地址,若是沒有找到,交換機會向全部其它端口廣播這個 ARP 請求。
1
2
3
4
|
Sender MAC: target:mac:address:here
Sender IP: target.ip.goes.here
Target MAC: interface:mac:address:here
Target IP: interface.ip.goes.here
|
如今咱們有了 DNS 服務器或者默認網關的 IP 地址,咱們能夠繼續 DNS 請求了:
當瀏覽器獲得了目標服務器的 IP 地址,以及 URL 中給出來端口號(http 協議默認端口號是 80, https 默認端口號是 443),它會調用系統庫函數 socket ,請求一個 TCP流套接字,對應的參數是 AF_INET/AF_INET6 和 SOCK_STREAM 。
到了如今,TCP 封包已經準備好了,能夠進行tcp三次握手(在地址解析完畢後首先進行的是三次握手,就是客戶端和服務器端只發送SYN包,創建三次鏈接後,在對HTTP的引用進行響應)
最終封包會到達管理本地子網的路由器。在那裏出發,它會繼續通過自治區域(autonomous system, 縮寫 AS)的邊界路由器,其餘自治區域,最終到達目標服務器。一路上通過的這些路由器會從IP數據報頭部裏提取出目標地址,並將封包正確地路由到下一個目的地。IP數據報頭部 time to live (TTL) 域的值每通過一個路由器就減1,若是封包的TTL變爲0,或者路由器因爲網絡擁堵等緣由封包隊列滿了,那麼這個包會被路由器丟棄。
客戶端發送一個 Client hello 消息到服務器端,消息中同時包含了它的 Transport Layer Security (TLS) 版本,可用的加密算法和壓縮算法。
服務器端向客戶端返回一個 Server hello 消息,消息中包含了服務器端的TLS版本,服務器選擇了哪一個加密和壓縮算法,以及服務器的公開證書,證書中包含了公鑰。客戶端會使用這個公鑰加密接下來的握手過程,直到協商生成一個新的對稱密鑰
HTTPD(HTTP Daemon)在服務器端處理請求/響應。最多見的 HTTPD 有 Linux 上經常使用的 Apache 和 nginx,以及 Windows 上的 IIS。
請求進入處理函數以後,若是客戶端所請求須要瀏覽的內容是一個動態的內容,那麼處理函數會相應的從數據源裏面取出數據,這個地方通常會有一個緩存,例如 memcached 來減少 db 的壓力,若是引入了 orm 框架的話,那麼處理函數直接向 orm 框架索要數據就能夠了,由 orm 框架來決定是使用內存裏面的緩存仍是從 db 去取數據,通常緩存都會有一個過時的時間,而 orm 框架也會在取到數據回來以後,把數據存一份在內存緩存中的。
orm 框架負責把面向對象的請求翻譯成標準的 sql 語句,而後送到後端的 db 去執行,db 這裏以 mysql 爲例的話,那麼一條 sql 進來以後,db 自己也是有緩存的,不過 db 的緩存通常是用 sql 語言 hash 來存取的,也就是說,想要緩存可以命中,除了查詢的字段和方法要同樣之外,查詢的參數也要徹底如出一轍纔可以使用 db 自己的查詢緩存,sql 通過查詢緩存器,而後就會到達查詢分析器,在這裏,db 會根據被搜索的數據表的索引創建狀況,和 sql 語言自己的特色,來決定使用哪個字段的索引,值得一提的是,即便一個數據表同時在多個字段創建了索引,可是對於一條 sql 語句來講,仍是隻能使用一個索引,因此這裏就須要分析使用哪一個索引效率最高了,通常來講,sql 優化在這個點上也是很重要的一個方面。
sql 由 db 返回結果集後,再由 orm 框架把結果轉換成模型對象,而後由 orm 框架進行一些邏輯處理,把準備好的數據,送到視圖層的渲染引擎去渲染,渲染引擎負責模板的管理,字段的友好顯示,也包括負責一些多國語言之類的任務。對於一條請求在 mvc 中的生命週期,在視圖層把頁面準備好後,再從動態腳本解釋器送回到 http 服務器,由 http 服務器把這些正文加上一個響應頭,封裝成一個標準的 http 響應包,再經過 tcp ip 協議,送回到客戶機瀏覽器。
當瀏覽獲得一個正確的 200 響應以後,接下來面臨的一個問題就是多國語言的編碼解析了,響應頭是一個 ascii 的標準字符集的文本,這個還好辦,可是響應的正文本質上就是一個字節流,對於這一坨字節流,瀏覽器要怎麼去處理呢,首先瀏覽器會去看響應頭裏面指定的 encoding 域,若是有了這個東西,那麼就按照指定的 encoding 去解析字符,若是沒有的話,那麼瀏覽器會使用一些比較智能的方式,去猜想和判斷這一坨字節流應該使用什麼字符集去解碼。相關的筆記能夠看這裏,瀏覽器對編碼的肯定
解決了字符集的問題,接下來就是構建 dom 樹了,在 html 語言嵌套正常並且規範的狀況下,這種 xml 標記的語言是比較容易的可以構建出一棵 dom 樹出來的,固然,對於互聯網上大量的不規範的頁面,不一樣的瀏覽器應該有本身不一樣的容錯去處理。構建出來的 dom 本質上仍是一棵抽象的邏輯樹,構建 dom 樹的過程當中,若是遇到了由 script 標籤包起來的 js 動態腳本代碼,那麼會把代碼送到 js 引擎裏面去跑,若是遇到了 style 標籤包圍起來的 css 代碼,也會保存下來,用於稍後的渲染。若是遇到了 img 等引用外部文件的標籤,那麼瀏覽器會根據指定的 url 再次發起一個新的 http 請求,去把這個文件拉取回來,值得一提的是,對於同一個域名下的下載過程來講,瀏覽器通常容許的併發請求是有限的,一般控制在兩個左右,因此若是有不少的圖片的話,通常出於優化的目的,都會把這些圖片使用一臺靜態文件的服務器來保存起來,負責響應,從而減小主服務器的壓力。
dom 樹構造好了以後,就是根據 dom 樹和 css 樣式表來構造 render 樹了,這個纔是真正的用於渲染到頁面上的一個一個的矩形框的樹,對於 render 樹上每個框,須要肯定他的 x y 座標,尺寸,邊框,字體,形態,等等諸多方面的東西,render 樹一旦構建完成,整個頁面也就準備好了,能夠上菜了。
須要說明的是,下載頁面,構建 dom 樹,構建 render 樹這三個步驟,實際上並非嚴格的前後順序的,爲了加快速度,提升效率,讓用戶不要等那麼久,如今通常都並行的往前推動的,現代的瀏覽器都是一邊下載,下載到了一點數據就開始構建 dom 樹,也一邊開始構建 render 樹,構建了一點就顯示一點出來,這樣用戶看起來就不用等待那麼久了。
當服務器提供了資源以後(HTML,CSS,JS,圖片等),瀏覽器會執行下面的操做:
瀏覽器的功能是從服務器上取回你想要的資源,而後展現在瀏覽器窗口當中。資源一般是 HTML 文件,也多是 PDF,圖片,或者其餘類型的內容。資源的位置經過用戶提供的 URI(Uniform Resource Identifier) 來肯定。
瀏覽器解釋和展現 HTML 文件的方法,在 HTML 和 CSS 的標準中有詳細介紹。這些標準由 Web 標準組織 W3C(World Wide Web Consortium) 維護。
不一樣瀏覽器的用戶界面大都十分接近,有不少共同的 UI 元素:
組成瀏覽器的組件有:
瀏覽器渲染引擎從網絡層取得請求的文檔,通常狀況下文檔會分紅8kB大小的分塊傳輸。
HTML 解析器的主要工做是對 HTML 文檔進行解析,生成解析樹。
解析樹是以 DOM 元素以及屬性爲節點的樹。DOM是文檔對象模型(Document Object Model)的縮寫,它是 HTML 文檔的對象表示,同時也是 HTML 元素面向外部(如Javascript)的接口。樹的根部是」Document」對象。整個 DOM 和 HTML 文檔幾乎是一對一的關係。
HTML不能使用常見的自頂向下或自底向上方法來進行分析。主要緣由有如下幾點:
因爲不能使用經常使用的解析技術,瀏覽器創造了專門用於解析 HTML 的解析器。解析算法在 HTML5 標準規範中有詳細介紹,算法主要包含了兩個階段:標記化(tokenization)和樹的構建。
瀏覽器開始加載網頁的外部資源(CSS,圖像,Javascript 文件等)。
此時瀏覽器把文檔標記爲「可交互的」(interactive),瀏覽器開始解析處於「推遲」模式的腳本(defer屬性的腳本文件),也就是那些須要在文檔解析完畢以後再執行的腳本。以後文檔的狀態會變爲「完成」(complete,DOMContentLoaded事件被響應),瀏覽器會進行「加載」事件(onload事件被響應)。
注意解析 HTML 網頁時永遠不會出現「語法錯誤」,瀏覽器會修復全部錯誤,而後繼續解析。
執行同步 Javascript 代碼。