瀏覽器輸入一個網址發生了什么(一) :揭秘 DNS 請求和解析全流程
從本系列文章開始將圍繞著“往瀏覽器輸入網址后發生了什么”介紹計算機網絡的相關基礎知識。
本文將介紹往瀏覽器中輸入一個網址后客戶端如何封裝http消息和發送dns請求查詢目標主機的ip的過程。
一、客戶端(如瀏覽器)解析url
url的組成部分如下所示:
客戶端會按照 協議、域名、文件路徑以及請求參數 對url進行解析(就是解析出以上信息)。
url如果沒有以/結尾表示訪問一個指定文件,以/結尾表示訪問指定目錄下的默認文件,例如:
- http://www.lab.glasscom.com/dir/ 訪問的是/dir 目錄下的index.html文件。
- http://www.lab.glasscom.com/dir 訪問的是 /dir文件。但是如果不存在 /dir文件,則會把它當做是目錄處理,因而會訪問/dir/下的默認文件
二、生成http請求消息
解析完url后,客戶端會根據請求的類型(這里以GET和POST為例)生成http請求消息。http請求消息和響應消息如下所示:
PS:get請求沒有消息體,post才有。
- 消息頭就是我們所說的header字段,包含諸如Content-Type、Cookie、Host等內容。
- 請求行的URI包括文件路徑和請求參數。
- 請求消息和響應消息的不同僅在第一行
- 一個請求消息中只能寫一個URI。
如下為get和post請求報文格式:
客戶端會根據上述(a)的格式生成http消息。
頭信息列舉
通用頭:
請求頭:
響應頭:
響應消息中的狀態碼概述:
三、向DNS服務器查詢域名對應IP
客戶端生成http消息后會委托操作系統將消息發送給web服務器。操作系統拿到http報文后為了將其發送給web服務器,會先根據域名查詢目標主機的IP地址。
1.簡單了解IP相關知識
在網絡中,所有設備都會被分配一個IP地址,IP地址相當于是服務器的坐標,客戶端得知這個坐標才能將消息發送到服務器主機。
實際的IP是一串32比特的數字(IPv4標準),并按8比特分為4組,每組用原點隔開,每組以十進制表示。除了IPv4標準外還有IPv6標準,IPv6下的IP地址則是128比特的長度用于支持更多可能的地址,這里只介紹IPv4下的內容。
IP地址分為網絡號和主機號兩部分,網絡號代表主機所在的子網網絡,子網與子網間通過路由器和集線器連接和通信,主機號代表某臺主機在子網中的具體位置。
IP地址被分為A~E類,每類的網絡號和主機號長度是不定的,例如C類IP的前3個字節是網絡號,第4個字節是主機號;而B類IP地址前2個字節是網絡號,后兩個字節是主機號。
IP地址會用附加信息來表示哪部分是IP的網絡號,哪部分是IP的主機號。這個附加信息就是子網掩碼。子網掩碼是一串與IP長度相同的32比特數字,也分為主機號和網絡號,其中左邊一半全為1,右邊一半全為0,全為1的部分為網絡號,全為0的部分為主機號。
例如:10.11.12.13/255.255.255.0 中 255.255.255.0就是子網掩碼,它的前3字節是1,第四字節為0,因此表示10.11.12.13中前3字節是網絡號,第4字節是主機號。
此外還可以用 網絡號比特數 表示子網掩碼,如 24。24表示前三字節為網絡號(8*3),16表示前兩個字節為網絡號(8*2),8表示只有第一個字節是網絡號。
域名也代表某一服務器的地址,但域名的使用是為了方便人類的記憶,而IP才是服務器所在主機的真正位置。
回到正題。
- 從域名解析為IP就涉及到DNS(域名解析)
- 為了得到域名對應的IP,客戶端(瀏覽器)在將http請求委托給操作系統發送前會查詢瀏覽器的DNS緩存找域名對應IP,如果命中緩存則客戶端會將IP帶給操作系統,沒有命中則操作系統會查找本機的DNS緩存,如果還是沒有命中,操作系統就會通過DNS解析器向DNS服務器發起DNS請求。
2.關于DNS解析器
DNS解析器本質上是操作系統socket庫中的一段程序,用于向dns服務器發送查詢請求并獲取域名對應的IP并返回給客戶端。而socket庫是用于調用網絡功能的通用程序組件。
客戶端通過DNS解析器發起dns請求的過程如下所示:
客戶端(瀏覽器)委托操作系統調用socket庫的gethostbyname()這個系統調用函數,此時用戶程序(瀏覽器)的工作會暫停并切換到操作系統的dns解析器運行,即用戶態切換到內核態。
DNS解析器根據瀏覽器提供的域名生成DNS查詢消息(DNS查詢報文)并交給操作系統中的TCP/IP協議棧,由協議棧通過網卡以UDP傳輸的方式發送給DNS服務器。在這里,操作系統必須得知道DNS服務器的IP,這樣操作系統才該把報文發送給那個DNS服務器,而這個DNS服務器的IP是已經事先設置好的,如下所示:
而這個設置好的DNS服務器一般是距離客戶端主機所在城市內不遠處的一個DNS服務器,我們稱之為本地DNS服務器。
發送DNS查詢報文后,DNS服務器會返回對應域名的IP地址給協議棧(至于DNS服務器根據域名返回對應ip的過程會在之后介紹),并按原路返回給DNS解析器,解析器將IP寫入到瀏覽器指定的內存地址中(從內核態切回用戶態),這樣當該瀏覽器待會再想知道這個域名的IP時就直接從用戶程序內存中取即可,無需在做DNS查詢。此外,操作系統和瀏覽器還會對這個域名與IP的映射關系做緩存,即使關閉了瀏覽器下次再打開并請求也無需做DNS查詢請求。除非緩存時間過期。
得到IP的值的瀏覽器終于可以開始發送HTTP請求消息。
四、DNS報文在多個DNS服務器間的傳遞
當客戶主機的操作系統將DNS查詢報文發送給本地DNS服務器到DNS服務器返回IP給客戶端主機這段期間發生了什么?這里需要介紹一下DNS查詢報文的內容和DNS服務器。
1.DNS查詢報文和回答報文
DNS 查詢和回答報文的格式是一致的,如下所示:
報文頭部的12個字節包含了一些控制信息如,報文是查詢還是回答報文,回答報文中告訴客戶端它訪問的是否為權威DNS服務器,客戶端是否希望DNS服務器提供遞歸查詢,以及該DNS服務器是否支持遞歸查詢,回答的RR數等。
查詢報文的實體部分需要包括2個部分:要查詢的域名和記錄類型。
記錄類型表示域名對應何種類型的記錄(即告訴DNS服務器,根據傳過來的域名,它應該返回什么類型的響應內容)。
dns服務器中保存著多條資源記錄(RR),資源記錄不只包括域名與IP的映射,但包括域名與其他信息的映射,而資源記錄中保存的到底是域名與什么信息的映射取決于記錄類型。
- 如類型是A表示該條記錄是域名對應的IP地址;
- 類型為MX,是該域名對應的郵箱服務器域名;
- 類型為CNAME,該記錄是一個別名域名對應的真正域名;
- 類型為NS時該記錄存著要查詢的域名是由哪個DNS服務器解析的(也就是記錄著一個目標DNS服務器的域名),此時這個DNS服務器應該還保存著目標DNS服務器的域名與IP的映射。
如下所示:
- baidu.com IN NS ns1.domain.com
- baidu.com IN NS ns2.domain.com
- ns1.domain.com IN A 111.222.111.111
- ns2.domain.com IN A 123.111.222.111
除了上述類型,還有其他的記錄類型。感興趣的朋友可以自行查閱。
回答報文會包含多條資源記錄(RR),每條資源記錄包括4個字段:(Name, Value, Type, TTL)。分別是域名,域名對應的值(如IP、權威DNS域名、郵箱域名等),記錄類型,記錄的有效時間。
2.域名層級
一個域名的層級從左到右依次提升,每個層級用.分開。以www.abc.com為例,com是頂級域名,com域的下一層是abc域,再下一層是www這個名字。
3.DNS服務器
DNS服務器本質是一個存儲著域名與相應信息映射關系的具有層級的、分布式數據庫集群。
(1) 存儲著域名與相應信息映射關系
DNS服務器里面保存的數據我們稱之為資源記錄,如下所示:
當帶有 域名、class和記錄類型 的請求報文到達DNS服務器后,它會根據這3個信息從資源記錄中找到這3個字段都匹配的記錄返回給客戶端。相當于 select `響應數據` from 表 where `域名`="xxx" and `class`="xxx" and `記錄類型`="xxx";
(2) 具有層級的、分布式數據庫集群
- 分布式:無論從服務器負載、數據備份和故障時高可用的角度來說,所有的域名信息不可能保存到一臺DNS服務器上,而是由多臺分布到世界上多臺DNS服務器共同維護。
- 具有層級:DNS服務器是具有層級的,其層級是根據域名的層級來劃分,由上到下分為 根域名DNS服務器、頂級域名DNS服務器和權威DNS服務器。
全世界的根域DNS服務器大概有十幾臺。
每一臺根域名服務器都知道所有頂級域名DNS服務器的域名和IP,而且不管哪一個本地域名服務器要對某個域名進行解析而自己無法解析都會首先求助于根域名服務器。
每一層DNS服務器都保存著下一層DNS服務器的所有IP地址,并且所有層級的每一臺DNS服務器會保存所有根域的DNS服務器IP。這么一來就能做到在本地DNS服務器能找到根域DNS服務器IP,再從根據服務器逐層往下找到其他DNS服務器。
當某一臺權威DNS服務器不能給出最后的查詢回答,就會告訴請求者下一步應該找到哪個權威DNS服務器。
用戶主機向本地域名服務器的查詢都是采用遞歸查詢(所以用戶主機只用發出一次DNS請求即可得到想要的IP地址,其他的請求交給本地DNS服務器和其他DNS服務器即可),本地域名服務器向根域名服務器的查詢通常采用迭代查詢,但也可以采用遞歸查詢。
如下圖所示:
有了上面的擴展知識后,我們再來說DNS報文發送到本地DNS服務器后發生了什么:
以www.lab.glasscom.com為例:
- DNS查詢報文到達本地DNS服務器后,如果本地DNS服務器沒有記錄目標域名對應的IP,則會將查詢轉發給根DNS服務器;
- 根域DNS服務器也沒有該域名的資源記錄,但判斷出其頂級域為com,所以會把com域的DNS服務器的IP返回給本地DNS服務器;
- 本地DNS服務器再根據這個IP向com域的DNS服務器發起查詢請求,但com域的DNS服務器也沒有www.lab.glasscom.com的資源記錄,但判斷出該域名的權威域名為glasscom.com,找到了glasscom.com域的DNS服務器的IP地址并返回。
- 本地DNS服務器向glasscom.com域的DNS服務器發起DNS查詢請求,查到了www.lab.glasscom.com的IP
- 最終本地DNS服務器將目標IP返回給客戶端主機,同時本地DSN服務器也會對該域名的IP映射進行緩存。下一次被訪問到相同的域名時直接查詢DNS緩存而不再向根域DNS服務器發起請求
上面這種DNS查詢方式是迭代查詢,每次都是由本地DNS服務器發起請求,此外還有其他查詢方式如遞歸查詢,以及直接查詢DNS緩存。
下面我們看看遞歸查詢和迭代查詢的區別:
- 遞歸查詢:由本機查詢本地DNS服務器,本地DNS服務器如果沒有命中目標域名則向根服務器請求,根服務器根據頂級域名找到對應的頂級域名服務器的IP并向該頂級域名服務器發送請求,同理頂級域名服務器再向對應的權威域名服務器發送DNS查詢請求的到目標地址IP,并將其原路返回給本機。
如下圖展示了遞歸查詢過程:
- 迭代查詢:迭代查詢就是上文中提到過的查詢方式,本機請求本地域名服務器,本地域名服務器請求根域名服務器拿到頂級域名服務器的ip,本地域名服務器再去請求頂級域名服務器拿到權威域名服務器的ip,本地域名服務器再去請求權威域名服務器拿到目標ip,再返回給本機。
如下圖: