瀏覽器是怎樣向網卡發送數據的

瀏覽器是怎樣向網卡發送數據的

從瀏覽器到瀏覽器內核


當我們在瀏覽器的地址欄中輸入地址並回車後,瀏覽器可能會做一些預處理,比如 Chrome 會根據歷史統計來預估所輸入字符對應的網站,比如輸入了“bai”,根據之前的歷史發現會有很大的概率會訪問 www.baidu.com ,因此就會在輸入回車前就馬上開始建立 TCP 鏈接甚至渲染了,這裏面還有很多其它策略,感興趣的同學推薦閱讀 High Performance Networking in Chrome(http://aosabook.org/en/posa/high-performance-networking-in-chrome.html)。

接着是輸入 URL 後的“回車”,這時瀏覽器會對 URL 進行檢查,首先判斷協議,如果是 http 就按照 Web 來處理,另外還會對這個 URL 進行安全檢查,然後直接調用瀏覽器內核中的對應方法,比如 WebView 中的 loadUrl 方法。

在瀏覽器內核中首先會檢查緩存,然後設置 UA 等 HTTP 信息,接着調用不同平臺下的網絡請求的方法。

瀏覽器和瀏覽器內核是兩個不同的概念,瀏覽器指的是 Chrome、Firefox,而瀏覽器內核則是 Blink、WebKit、Gecko等,瀏覽器內核只負責渲染,GUI 及網絡連接等跨平臺工作則是由瀏覽器實現的

發送HTTP 請求


因爲網絡的底層實現與內核相關,所以在這裏需要針對不同平臺進行處理,從應用層角度來看主要是做兩件事情:

  1. 通過 DNS 查詢 IP

  2. 通過 Socket 發送數據

接下來就分別介紹這兩方面的內容。

DNS 查詢


DNS 查詢其實是基於 UDP 來實現的,這裏我們通過一個具體例子來了解它的查找過程,以下是使用 dig fex.baidu.com +trace 命令得到的結果:

 1> dig fex.baidu.com +trace
 2
 3; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.6 <<>> fex.baidu.com +trace
 4;; global options: +cmd
 5.            11950   IN  NS  f.root-servers.net.
 6.            11950   IN  NS  k.root-servers.net.
 7.            11950   IN  NS  l.root-servers.net.
 8.            11950   IN  NS  m.root-servers.net.
 9.            11950   IN  NS  b.root-servers.net.
10.            11950   IN  NS  c.root-servers.net.
11.            11950   IN  NS  e.root-servers.net.
12.            11950   IN  NS  a.root-servers.net.
13.            11950   IN  NS  g.root-servers.net.
14.            11950   IN  NS  d.root-servers.net.
15.            11950   IN  NS  i.root-servers.net.
16.            11950   IN  NS  h.root-servers.net.
17.            11950   IN  NS  j.root-servers.net.
18;; Received 251 bytes from 192.168.0.1#53(192.168.0.1) in 12 ms
19
20com.            172800  IN  NS  a.gtld-servers.net.
21com.            172800  IN  NS  b.gtld-servers.net.
22com.            172800  IN  NS  c.gtld-servers.net.
23com.            172800  IN  NS  d.gtld-servers.net.
24com.            172800  IN  NS  e.gtld-servers.net.
25com.            172800  IN  NS  f.gtld-servers.net.
26com.            172800  IN  NS  g.gtld-servers.net.
27com.            172800  IN  NS  h.gtld-servers.net.
28com.            172800  IN  NS  i.gtld-servers.net.
29com.            172800  IN  NS  j.gtld-servers.net.
30com.            172800  IN  NS  k.gtld-servers.net.
31com.            172800  IN  NS  l.gtld-servers.net.
32com.            172800  IN  NS  m.gtld-servers.net.
33com.            86400   IN  DS  30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
34com.            86400   IN  RRSIG   DS 8 1 86400 20200625050000 20200612040000 48903 . OwfRn9tBOE2btL/z3HG5PQVyTXu2OUcZGLi9svkHFV0tomeI1p9bHhqr GF/UDjf5a8VXNRoaSsSEQfgqwJT3UAOANK1vb3e+5jH2bV3Hg6/MAGG0 SuBfKv8Y1fjGgiLNC3NKmTWJ28WABHngymnGDpuqoC6xKmkVoD14ON7E uHbBGxC0Uxt6D5R3WfbAAfbzZXzyPcD3WK1OpGaL6ASMB2xvdAZIkp/Z l8QDmqZd86RX7haiVhxVG0mMrWxsN7XL2jVyRRFFl9UkApMk9/thPwNK Rgkd4BPCvMPZTvsb+mPZA4InLxP6oPliZQm5sIWH8fEiyS+LgEReROzG sqrpyw==
35;; Received 1173 bytes from 198.97.190.53#53(h.root-servers.net) in 206 ms
36
37baidu.com.        172800  IN  NS  ns2.baidu.com.
38baidu.com.        172800  IN  NS  ns3.baidu.com.
39baidu.com.        172800  IN  NS  ns4.baidu.com.
40baidu.com.        172800  IN  NS  ns1.baidu.com.
41baidu.com.        172800  IN  NS  ns7.baidu.com.
42CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM
43CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20200618045106 20200611034106 39844 com. nL7GSwad11x22Ff4/a3sjIA27DplTa0SZWNb9jnTs0+PYEehVKCT4a2g TWgi5YHeqolDbwsK9oy7Hy1ZO3yhlhWUUAIyE5DE+iKuJCnD6fIvmXdq lXsBBvUHK6wtHzIAPJ8PbCAl/PwSNjpZUZvv4YcEtLWU14yTsPPAM/wB BxatwSt88sQrwYrLKqjnojEsmKVX1yi98pdT87BI/zKxzQ==
44HPVU6NQB275TGI2CDHPDMVDOJC9LNG86.com. 86400 IN NSEC3 1 1 0 - HPVVN3Q5E5GOQP2QFE2LEM4SVB9C0SJ6 NS DS RRSIG
45HPVU6NQB275TGI2CDHPDMVDOJC9LNG86.com. 86400 IN RRSIG NSEC3 8 2 86400 20200619061821 20200612050821 39844 com. Iz4sOw47dg/aDbs/T9JSAXDiE88bqoj/kYbDQW5dO9NnQicyC5ZqEj0o l1hxJHirVmJtCIXevkSy3eH1rrOH/Ni+oLlWZEBzQnucFK1C4WdBylF2 0OsgaG/AyHSD+9tWgMcQY+i28WBpxmxXvDLHY0oWb89UHMpcduqCh5+n YXnbOHzjvaER/hX1ljveDo0z+HJIBtgY6/0NeFFY0ZWkcA==
46;; Received 761 bytes from 192.43.172.30#53(i.gtld-servers.net) in 248 ms
47
48fex.baidu.com.        600 IN  CNAME   sugar.n.shifen.com.
49n.shifen.com.        86400   IN  NS  ns5.n.shifen.com.
50n.shifen.com.        86400   IN  NS  ns2.n.shifen.com.
51n.shifen.com.        86400   IN  NS  ns4.n.shifen.com.
52n.shifen.com.        86400   IN  NS  ns3.n.shifen.com.
53n.shifen.com.        86400   IN  NS  ns1.n.shifen.com.
54;; Received 241 bytes from 112.80.248.64#53(ns3.baidu.com) in 32 ms

可以看到這是一個逐步縮小範圍的查找過程,首先由本機所設置的 DNS 服務器( 192.168.0.1 )向 DNS 根節點查詢負責 .com 區域的域務器,然後通過其中一個負責 .com 的服務器查詢負責 baidu.com 的服務器,最後由其中一個 baidu.com 的域名服務器查詢 www.baidu.com 域名的地址。

你在查詢某些域名的時會可能會發現和上面不一樣,最後將會看到有個奇怪的服務器搶先返回結果。。。

 這裏爲了方便描述,忽略了很多不同的情況,比如 127.0.0.1 其實走的是 loopback,和網卡設備沒關係;比如 Chrome 會在瀏覽器啓動的時預先查詢 10 個你有可能訪問的域名;還有 Hosts 文件、緩存時間 TTL(Time to live)的影響等。

通過 Socket 發送數據


有了 IP 地址,就可以通過 Socket API 來發送數據了,這時可以選擇 TCP 或 UDP 協議,具體使用方法這裏就不介紹了,推薦閱讀 Beej’s Guide to Network Programming(http://beej-zhcn.netdpi.net/)。

HTTP 常用的是 TCP 協議,由於涉及到 TCP 協議的具體細節的資料很容易就能找到,所以本文就不贅述了,只在這裏談一下 TCP 的 隊首阻塞 問題:假設客戶端發送了 3 個 TCP 片段(segments),編號分別是 1、2、3,如果編號爲 1 的包傳輸時丟了,那麼即便編號 2 和 3 已經到達也只能等待,因爲 TCP 協議需要保證先後順序,這個問題在 HTTP pipelining 下更嚴重,因爲 HTTP pipelining 可以讓多個 HTTP 請求通過一個 TCP 發送,比如發送兩張圖片,可能第二張圖片的數據已經全收到了,但還得等第一張圖片的數據傳到。

爲了解決 TCP 協議的性能問題,Chrome 團隊提出了 QUIC 協議,它是基於 UDP 實現的可靠傳輸,比起 TCP,它能減少很多往返(round trip)時間,還有前向糾錯碼等功能。目前 Gmail、Google Search、blogspot、Youtube 等幾乎大部分 Google 產品都在使用 QUIC,你可以在 Chrome 中的 chrome://flags/#enable-quic 頁面找到它的配置。

雖然國內很多大廠也在研究 QUIC 的應用,但離大範圍普及還有較長的一段距離,因爲如果針對 TCP 進行優化,需要升級系統內核。

瀏覽器對同一個域名有連接數是有限制得,[一般是 6 個(http://www.browserscope.org/?category=network&v=top),Chrome 團隊曾做過實驗,發現從 6 改成 10 後性能反而下降了,造成這個現象的因素有很多,如建立連接的開銷、擁塞控制等問題,而像 SPDY、HTTP 2.0 協議儘管只使用一個 TCP 連接來傳輸數據,但性能反而更好,而且還能實現請求優先級。

另外,因爲 HTTP 請求是純文本格式的,所以在 TCP 的數據段中可以直接分析 HTTP 的文本。

Socket 在內核中的實現


前面說到瀏覽器的跨平臺庫通過調用 Socket API 來發送數據,那麼 Socket API 是如何實現的呢?

以 Linux 爲例,它實現在 socket.c(http://lxr.linux.no/linux+v3.14.4/net/socket.c) 中,如果你想深入研究一下,推薦看 Linux kernel map(http://www.makelinux.net/kernel_map/),它標註出了關鍵路徑的函數,方便學習從協議棧到網卡驅動的實現

底層網絡協議的具體例子


接下來如果繼續介紹 IP 協議和 MAC 協議可能會把大家搞暈,所以下面用 tcpdump 來通過具體例子講解,以下是在請求百度首頁時抓取到的網絡數據:

瀏覽器是怎樣向網卡發送數據的
可以看到最前面的三次通信是 TCP 協議的三次握手過程,在第四次通信中被選中的部分爲 HTTP 協議(Hypertext Transfer Protocol),在 HTTP 之前有 54 字節(0x36),這就是底層網絡協議所帶來的開銷,我們接下來對這些協議進行分析。

在 HTTP 之上是 TCP 協議(Transmission Control Protocol),它的具體內容如下圖所示:
瀏覽器是怎樣向網卡發送數據的

通過底部的二進制數據,可以看到 TCP 協議是加在 HTTP 文本前面的,它有 20 個字節,其中定義了本地端口(Source port)和目標端口(Destination port)、順序序號(Sequence Number)、窗口長度等信息,以下是 TCP 協議各個部分數據的完整介紹:

瀏覽器是怎樣向網卡發送數據的
具體每個字段的作用這裏就不介紹了,感興趣的同學可以通過閱讀 RFC 793(http://tools.ietf.org/html/rfc793),並結合抓包分析來理解

需要注意的是,在 TCP 協議中並沒有 IP 地址信息,因爲這是在上一層的 IP 協議中定義的,如下圖所示:

瀏覽器是怎樣向網卡發送數據的
IP 協議同樣是在 TCP 前面的,它也有 20 字節,在這裏指明瞭版本號(Version)爲 4,源(Source) IP 爲 192.168.1.106,目標(Destination) IP 爲 119.75.217.56,因此 IP 協議最重要的作用就是確定 IP 地址。

因爲 IP 協議中可以查看到目標 IP 地址,所以如果發現某些特定的 IP 地址,某些路由器就會。。。

但是,光靠 IP 地址是無法進行通信的,因爲 IP 地址並不和某臺設備綁定,比如你的筆記本的 IP 在家中是 192.168.0.11,但到公司就變成 10.0.11.11 了,所以在底層通信時需要使用一個固定的地址,這就是 MAC(media access control) 地址,每個網卡出廠時的 MAC 地址都是固定且唯一的。

因此再往上就是 MAC 協議,它有 14 字節,如下所示:

瀏覽器是怎樣向網卡發送數據的
當一臺電腦加入網絡時,需要通過 ARP 協議告訴其它網絡設備它的 IP 及對應的 MAC 地址是什麼,這樣其它設備就能通過 IP 地址來查找對應的設備了。

現在我們搞清了標題中的問題,不過這裏面還有大量的細節沒介紹,建議大家通過下面的書籍進一步學習。

擴展學習


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章