本文的目標是以「輸入 URL 後發生了什麼」這個經典面試題爲引子,寫一篇既可以涵蓋面試中大部分網絡試題,又可以將「輸入 URL 後發生什麼」講得有深度的文章。之前寫過一篇相似的文章,但實在過於簡單。另外,HTTPS 逐漸普及,文章中沒有這部分過程也說不過去。不想修改原來的文章,就從新寫一篇吧。文中以我所在的項目「興趣部落」的官網 https://buluo.qq.com/index.html 爲例子。html
解析完要訪問的目標服務器是啥了,接下來瀏覽器就會用 HTTP 協議生成請求消息去 web服務器請求資源,消息格式以下:web
請求信息主要包括:面試
對應的,響應消息也有 3 個部分組成:瀏覽器
用圖表示:緩存
生成 HTTP 消息後,瀏覽器委託操做系統將消息發送給 web服務器。而經過 web服務器的名稱是無法找到服務器在哪的,比如知道一我的的名字無法找到他家在哪同樣,網絡中的地址是用 IP 地址表示的,因此要想跟服務器通訊,得先找到它的 IP 地址,使用 DNS(Domain Name System,域名服務系統) 服務器能夠將 web服務器名稱轉換成 IP 地址。那這個過程是怎樣的呢?安全
操做系統有一個 Socket 庫,這個庫中的程序主要是讓應用程序調用操做系統的網絡功能,而在這些功能中,瀏覽器須要調取操做系統的 DNS 解析功能。DNS 解析器生成一條表示「告訴我 https://buluo.qq.com/index.html 的 IP 地址」的消息,而後委託操做系統的協議棧發送 UDP 消息到 DNS 服務器。那這條消息是如何發送到 DNS 服務器又是如何將 IP 地址返回的呢?服務器
首先介紹下操做系統中 DNS 解析器發送給 DNS 服務器的消息內容,消息中包含 1)域名:buluo.qq.com;2)Class: IN,表明當前的網絡是因特網,DNS 設計之初還考慮了其餘網絡,雖然如今只有互聯網,但這個字段仍是保留了下來;3)記錄類型:A,表示域名對應的是 IP 地址,由於 DNS 還能解析其餘地址,好比類型爲 MX 時 DNS 服務器會查詢郵件服務器地址。DNS 服務器中維護一張表,表的每一項包含上面三個字段還有服務器地址,當域名、Class、記錄類型所有匹配時,DNS 服務器返回地址,在例子中會返回興趣部落首頁的 IP 地址。網絡
但這個時候問題來了,世界上有不可勝數的服務器,將這些全部的服務器信息都保存在一個 DNS 的表中確定是不現實的,因此確定有不少臺 DNS 服務器一塊兒配合完成這個域名解析過程的,那具體過程是什麼樣的呢?併發
首先,DNS 服務器中的全部信息都是按照域名來劃分層次的,這個層次是用 .
來分隔的,越靠右層次越高,好比 「buluo.qq.com」 中 「com」 層次最高,「qq」 次之,「buluo」 最後,其中每一層都被稱爲「域」,好比 「com 域」下是 「qq」 域,再下是 「buluo」 域,域的層次劃分是爲了更好地分配給不一樣國家、公司和組織等,典型的例子像南京市政府的官網:「www.nanjing.gov.cn」,「cn」 表明中國這個國家的域,「gov」 表明這個國家下的政府組織,「nanjing」 表明南京市政府。域有層次之分,那 DNS 服務器呢?規定將管理下級域的 DNS 服務器的 IP地址註冊到上級的 DNS 服務器中,好比管理 「buluo.qq.com」 這個域的 DNS 服務器的 IP地址須要註冊到 「qq.com」 域的 DNS 服務器中,以此類推,一直到「根域」,就是 「cn」、「com」 這類域的上一層次,根域中就保存了 「cn」、「com」 等域名的 DNS 服務器信息。此外,還須要將根域的 DNS 服務器信息保存在全部的 DNS 服務器中,這樣只要找到一臺 DNS 服務器就能夠順藤摸瓜找到下層任何一個 DNS 服務器。知道了域的層次劃分以及 DNS 服務器的分佈,下面就正式介紹如何尋找到相應的 DNS 服務器並獲取 IP 地址。dom
首先,客戶端會訪問最近的一臺 DNS 服務器,但因爲這臺 DNS 服務器上沒有 「buluo.qq.com」 這個域名的對應的信息,因此就向根域 DNS 服務器發請求詢問,但根域中也沒有,但斷定這個域名是屬於 「com」 域的,因此就返回其管理的 「com」 域的 DNS 服務器的 IP 地址,意思是「雖然我不知道,但你能夠去某某處問問,他應該知道」。而後 最近的那個 DNS 服務器又向 「com」 域的 DNS 服務器發請求,同理,也不知道,而後返回 「qq.com」 域的 DNS 服務器,而後這臺最近的 DNS 服務器又向 「qq.com」 域 DNS 服務器發請求,仍然沒有,直到最後,向 「buluo.qq.com」 這個域下的 DNS 服務器發請求才拿到 IP 地址。接着,這臺最近的 DNS 服務器將得到的 「buluo.qq.com」 的 IP 地址返回給客戶端,客戶端再拿着這個 IP 地址去請求資源。以上的過程用圖表示以下:
以上就是經過 DNS 服務獲取目標服務器 IP 地址的過程,能夠說是很是耗時,爲了優化性能,DNS 服務器會對中間的查詢結果作個緩存,爲了保存緩存的實時性,每隔一段時間就會將緩存設爲過時。
如今客戶端拿到了目標服務器的 IP 地址,下面就要與其鏈接併發送消息了,這個過程一樣不是瀏覽器作的,而是委託協議棧來完成的,具體過程是:
在「委託協議棧發送消息」部分簡單地提了下客戶端和服務端利用套接字進行鏈接,那這個鏈接具體是什麼樣的呢?
首先什麼是套接字?套接字其實就是個放在內存的備忘錄,協議棧在發送數據時先看一眼備忘錄,瞭解這個數據是發到哪一個端口,當數據發送出去後,這個備忘錄還得記錄什麼時間收到響應、何時斷開等控制信息,協議棧須要根據這些信息來決定下一步作什麼。
客戶端和服務端的鏈接是經過套接字鏈接的,那「鏈接」又是什麼意思呢?鏈接其實是客戶端和服務端互相交換控制信息的過程,控制信息主要包含兩種,一種是上面提到的套接字裏要來幫助協議棧進行下一步操做的信息,另外一種是客戶端和服務端通訊時交換的控制信息,這種控制信息就是咱們俗稱的 TCP 頭部。 那鏈接的過程是怎樣的呢?
這個鏈接過程就是咱們平時常常聽到的三次握手。
整個過程用圖表示以下:
上面的過程是最簡單的 HTTP 三次握手,但如今愈來愈多的網站使用了 HTTPS 協議,那與 HTTP 鏈接有什麼不一樣呢?
先介紹一下什麼是 HTTPS。HTTPS 正如其名字,HTTP 表明其並非本身建立一個新的協議,而是創建在 HTTP 的基礎之上,S 表明其是安全的,如何保證安全?利用 SSL/TLS。SSL(Secure Sockets Layer,安全套接層)是網景設計的安全傳輸協議,經歷了 1.0、2.0 和 3.0 版本,但由於 1.0 有嚴重安全缺陷,因此從未公佈。後來 IETF 將 SSL 標準化,稱爲 TLS(Transport Layer Security, 傳輸層安全協議) ,TLS 1.0 與 SSL 3.0 差異很小。TLS 經歷了 1.0、1.1 到如今最新的 1.2。在 HTTPS 通訊中具體使用哪種還要看客戶端和服務端的支持程度。那 SSL/TLS 在網絡模型中屬於哪一層呢?直接上圖:
在客戶端和服務端經過 HTTPS 鏈接的過程當中,除了正常的 HTTP 鏈接中的事情,還有身份驗證和加密信息兩件事,下面看看具體過程(更詳細內容能夠查看標準:RFC5246)。
Client Hello:此次握手是客戶端向服務端發起加密通訊請求,請求中包含如下關鍵信息:
Client Key Exchange Message:這一步很是關鍵,客戶端會生成 premaster secret(預主密鑰),爲何叫 premaster secret?由於後面客戶端和服務端會根據 premaster secret 和前面過程當中兩個隨機數共同生成一個 master secret(主密鑰,48字節),後面通訊的安全全靠這個 master secret。前兩個隨機數客戶端和服務端都知道了,這個步驟最主要的就是協商一個 premaster secret,這個過程叫作「密鑰交換」,這裏介紹兩個方法:
用圖表示一下就是:
整個握手過程總結一下就是:
以上就是握手的整個通訊細節,但細心的同窗可能會發現少了一個重要步驟,客戶端收到服務器發來的證書時是如何斷定對方就是本身想要找的服務器呢?這時候就要驗證證書的有效性,證書就像現實中的身份證,能夠確認某個網站的確是我要訪問的網站。那怎麼驗證證書的有效性呢?首先,數字證書和身份證同樣由權威機構簽發,不一樣的是身份證只能由政府簽發,而數字證書由 CA(Certification Authorities,數字證書認證機構)簽發,Mac 用戶能夠經過「文件-應用程序-實用工具-鑰匙串訪問」來查看根 CA,根 CA 能夠簽發其餘 CA,因此一個網站的簽發者不是根 CA 也不要緊,只要這個 CA 的簽發者是根 CA 也行。瞭解了 CA,下面看一下證書包含什麼,先看圖:
證書中包含:網站的基本信息、網站的公鑰、CA 的名字等信息(詳細請看 X.509),而後 CA 根據這幾個內容生成摘要(digest),再對摘要用 CA 的私鑰加密,加密後的結果即數字簽名,最後將數字簽名也放入到證書中。那麼當系統收到一個證書後,先用公鑰解密,解得開說明對方是由權威 CA 簽發的,而後再根據證書的信息生成摘要,跟解密出來的摘要對比。
創建鏈接以後,客戶端和服務端即可以開始進行數據傳輸。一樣,瀏覽器委託協議棧來幫忙收發消息,協議棧收到消息後不會當即發送出去,而是先放入到緩存區中,由於向協議棧發送的數據長度由瀏覽器控制,若是協議棧一收到數據就發送出去,那麼可能會發送大量小包,致使網絡效率下降,因此協議棧通常會等數據量積累到必定程度再發送出去,那這個程度具體是啥樣?
首先,在以太網中,一個包的MTU(Maximum Transmission Unit,最大傳輸單元)是 1500 字節,除去 TCP、IP 頭部的 40字節,MSS(Maximum Segment Size,最大分段大小)就是 1460 字節,但由於加密須要,頭部可能會增長,相對的 MSS 就會減小。當緩存區內的數據接近 MSS 時再發送,能夠避免發送小包。可是若是數據量原本就很小,或者應用程序發送數據的頻率很小,那協議棧就不得不等很長時間,因此協議棧內部還有一個定時器,必定時間以後就會將包發送出去。若是數據較小,那就幾個拼個車,放在一個包裏發出去,若是數據很大,就要進行拆分。大概是下面這樣:
本地一切就緒以後,協議棧就會將消息發送出去,這時還沒完,客戶端還要確保服務器收到了消息。咱們一直都說 TCP 是面向鏈接的協議,由於它能夠糾正丟包錯誤、鏈接失敗提示等等,使得傳輸更加可靠。那具體又是怎麼樣的呢?
首先 TCP 模塊在拆分數據時會先算好每一塊數據至關於從頭開始是第幾個字節,而後將這個數字寫入到 TCP 頭部的「序號」字段中,經過這個字段,接收方就能知道包有沒有丟失,好比一個消息長度爲 4380(1460 * 3),那麼這條消息就被拆分到三個數據塊中,三個數據塊的 TCP 頭部的「序號」依次是 0、1460 和 2920,因此接收方先收到一個序號爲 0 的包,再收到一個序號爲 2920 的包,可是沒收到序號爲 1460 的包,說明這個包丟失了,現實中的序號爲了安全不會從 0 開始,而是以一個隨機數做爲初始值。若是確認沒有遺漏,那麼接收方會將到目前爲止收到的數據長度加起來,寫入 TCP 的 「ACK 號」中發送給對方,注意 「ACK 號」與 ACK 標記位不是一回事,前者是數字,後者就是一個比特的標記位,可是 「ACK 號」只有在 ACK 標記位爲 1 是纔有效。
當數據發送完畢後,一方(多是客戶端,多是服務端)就會發起斷開鏈接過程。這個過程也是你們很熟悉的,即四次揮手。下面以客戶端發起斷開請求爲例:
以上就是輸入 URL 後大概發生的一些事情,可是從面試角度看,仍然還有不少部分沒有涉及。後續還會繼續更新這篇文章,添加一些重要內容,這裏先挖個坑:
好,坑就挖這麼多,再多怕本身不想填,等填完再繼續挖。