瀏覽器輸入url後發生了什麼

從url到ip地址

 

dns解析

  1. 瀏覽器檢域名是否在快取當中
  2. 如果快取中沒有,就去調用 gethostbyname 庫函數進行查詢。
  3.  gethostbyname 函數在試圖進行DNS解析之前首先檢查域名是否在本地 Hosts 里
  4. 沒有快取,也沒有在 hosts 里找到,則將會向 DNS 伺服器發送一條 DNS 查詢請求(UDP,53埠)
  5. 查詢本地 DNS 伺服器
  6. 如果 DNS 伺服器和我們的主機在同一個子網內,系統會按照下面的 ARP 過程對 DNS 伺服器進行 ARP查詢
  7. 如果 DNS 伺服器和我們的主機在不同的子網,系統會按照下面的 ARP 過程對默認網關進行查詢

 

ARP

  1. 首先查詢 ARP 快取,如果快取命中,我們返回結果:目標 IP = MAC,如果快取沒有命中:
  2. 向本網段的所有主機發送ARP數據包
  3. 當本網路的所有主機收到該ARP數據包時,首先檢查數據包中的IP地址是否是自己的IP地址,如果不是,則忽略該數據包,如果是,則首先從數據包中取出源主機的IP和MAC地址寫入到ARP列表中,如果已經存在,則覆蓋,然後將自己的MAC地址寫入ARP響應包中,告訴源主機自己是它想要找的MAC地址。
  4. 源主機收到ARP響應包後。將目的主機的IP和MAC地址寫入ARP列表,並利用此資訊發送數據。如果源主機一直沒有收到ARP響應數據包,表示ARP查詢失敗。
    廣播發送ARP請求,單播發送ARP響應

 

遞歸查詢迭代查詢

之後從DNS伺服器中獲得域名對應的ip地址

 


 

 從tcp數據報到比特流

當瀏覽器得到了目標伺服器的 IP 地址,以及 URL 中給出來埠號(http 協議默認埠號是 80, https 默認埠號是 443),它會調用系統庫函數socket ,請求一個 TCP流套接字,對應的參數是  AF_INET/AF_INET6  和  SOCK_STREAM  。

作業系統的任務:

  • 這個請求首先被交給傳輸層,在傳輸層請求被封裝成 TCP segment。目標埠會被加入頭部,源埠會在系統內核的動態埠範圍內選取(Linux下是ip_local_port_range)
  • TCP segment 被送往網路層,網路層會在其中再加入 IP 頭部(可能會切片),裡面包含了目標伺服器的IP地址以及本機的IP地址,把它封裝成IP packet

集成網卡的任務(實現乙太網協議,負責組裝成幀、串列/並行轉換、快取數據:由於網路上的數據率和電腦匯流排上的數據率並不相同,因此在網卡中必須裝有對數據進行快取的存儲晶片):

  • 這個 IP packet 接下來會進入鏈路層,鏈路層會在封包中加入 frame 頭部,也就是封成乙太網幀。裡面包含了本地內置網卡的MAC地址以及網關(本地路由器)的 MAC 地址。像前面說的一樣,如果內核不知道網關的 MAC 地址,它必須進行 ARP 廣播來查詢其地址。
  • 集成網卡將乙太網幀編碼成適合在線路上進行傳輸的物理訊號(比特流),並將比特流從網路介面中發出。

再通過數據機把數字訊號轉換成模擬訊號從網線發出

 


 

從路由器到路由器

如上圖,比特流在路由器中剖成ip數據報之後再提取出目標ip地址,並根據分組轉發協議進行查找:

 

分組轉發協議

  1. 從數據報的首部提取ip地址D,得出目的網路地址為N
  2. 若N就是直接相連的某個主機,直接交付
  3. 若路由表中有目的地址為D的特定主機路由,則把數據報傳送給路由表中的下一跳路由器
  4. 若路由器中有到達目的網路N的路由,則把數據報傳送給路由表中所指明的下一跳路由器
  5. 若路由表中有一個默認路由,則把數據報傳送給路由表中所指明的下一跳路由器
  6. 使用ICMP差錯報告報文報錯

 

通過分組轉發協議,得到相應的路由器或主機ip後,不是填入ip數據報,而是進行ARP將該ip地址轉化為物理地址。之後將物理地址包入乙太網幀,轉成比特流之後繼續發送。

在路由器之間移動的過程中可能會經過一些AS,順便一提AS的路由選擇協議有RIP(UDP)和OSPF(IP),AS間是BGP(TCP)

 


 

從網線到Socket

  1. 數據機把模擬訊號轉換回數字訊號
  2. 經過網卡拆解成IP packet存入網卡的緩衝區隊列
  3. 之後發出中斷,CPU保存運行現場後響應中斷,運行網卡中斷程式(這裡以epoll為例)(這裡已經變成TCP segment了,之後epoll流程處理的是TCP segment,具體怎麼變成TCP segment的我目前還不清楚,有知道的請告訴我一聲):
  4. 添加socket,並加入到eventpoll的等待隊列中(第一次發起才有添加socket的操作),將網卡的數據寫入到對應 socket 的接收緩衝區裡面;
  5. 修改 rdlist,並喚醒 eventpoll 等待隊列中的進程對socket進行處理

(epoll的具體流程可以看這裡epoll的實現原理

 

這一部分將會在後面不停進行以傳輸TCP segment 

 


 

從socket到http或https

 這裡你的http伺服器(可以是nginx也可以是tomcat)就會開始接受socket連接(也就是tcp連接,socket是對tcp和udp的封裝),這裡如果是tomcat,源碼中會有 .accept 和 .register 的調用,在經過三次握手之後

 如果你是採用https,則會創建ssl連接:

  1. 客戶端通過發送 Client Hello 報文開始 SSL通訊。報文中包含客戶端支援的 SSL的指定版本、加密組件(Cipher Suite)列表(所使用的加密演算法及密鑰長度等)。
  2. 伺服器可進行 SSL通訊時,會以 Server Hello 報文作為應答。和客戶端一樣,在報文中包含 SSL版本以及加密組件。伺服器的加密組件內容是從接收到的客戶端加密組件內篩選出來的。
  3. 之後伺服器發送 Certificate 報文。報文中包含公開密鑰證書。(這裡的證書是怎麼來的,如何驗證的我們一會再說,我們只要知道它包含伺服器的公鑰就夠了)
  4. 最後伺服器發送 Server Hello Done 報文通知客戶端,最初階段的 SSL握手協商部分結束
  5. SSL第一次握手結束之後,客戶端以 Client Key Exchange 報文作為回應。報文中包含通訊加密中使用的一種被稱為 Pre-master secret 的隨機密碼串。該隨機密碼串已用步驟 3 中的公開密鑰進行加密。
  6. 接著客戶端繼續發送 Change Cipher Spec 報文。該報文會提示伺服器,在此報文之後的通訊會採用 Pre-master secret 密鑰加密。
  7. 客戶端發送 Finished 報文。該報文包含連接至今全部報文的整體校驗值。這次握手協商是否能夠成功,要以伺服器是否能夠正確解密該報文作為判定標準。
  8. 伺服器用自己的私鑰解開步驟5中的報文,得到隨機密碼串。伺服器同樣發送 Change Cipher Spec 報文。
  9. 伺服器同樣發送 Finished 報文。

連接完成之後繼續接收從socket拿到的數據,如果是https,後續的數據都要進行簡單的解密(加密方式是使用前面獲得的隨機密碼串作為密碼參數進行對稱加密

之後就是http伺服器對傳來的數據進行封裝,封裝成http請求類等。

 

總結一下,這裡這些操作包括ssl連接主要是http伺服器對socket的調用(如Java寫的Tomcat調用的accept、register、select等,為NIO部分的知識),並封裝http或https對象,感興趣可以看一下我的這篇源碼解析:jdk下httpserver源碼解析,https部分詳情請見:Https原理

 


 

從http到servlet

之後就是容器的各種封裝了,下圖是Tomcat的架構圖,這裡會送到最右邊的Container封裝成servlet。這裡本來可以寫不少東西,不過我沒研究過,就不多說了。

前面的Connector部分的解析的話可以看這裡:Tomcat中對NIO的應用

 

 


 

從servlet到springMVC框架

之後就是SpringMVC對Servlet的封裝了,具體就不細說了,之後就是常見的SpringMVC的流程了:

 

至於返回到瀏覽器的流程就大同小異了

本文是我當前水平對這個問題所能做到的最詳細的解答了,後續如果有更加深入(例如我一直沒看的linux內核)的理解的話再更新吧。

 

因為我是後端的,就不提瀏覽器解析部分了,感興趣的可以看這裡:What-happens-when,裡面還有從鍵盤按鍵中斷開始聊起的,還蠻有意思的。

 

最後慣例附一圖:太棒了,我逐漸理解一切.jpg(順便佩服找不到實習還花了幾天水部落格的自己)

 

Tags: