概覽
本次探索之旅從用戶在瀏覽器中輸入網址(URL)開始。
生成HTTP請求消息
瀏覽器的工作會從對用戶輸入的網址進行解析開始。瀏覽器如何解析網址是我們的第一個看點。然后瀏覽器會根據網址的含義來生成請求消息,而請求消息實際的樣子就是我們的第二個看點。
向DNS服務器查詢Web服務器的IP地址
瀏覽器如何進行這一操作也是看點之一
全世界DNS服務器的大接力
全世界共有上萬臺的DNS服務器,它們相互借力才能完成IP地址的查詢,而它們進行接力的方法也是本章看點之一。
委托協議棧發(fā)送消息
查詢到IP地址之后,瀏覽器就可以將消息委托給操作系統發(fā)送給Web服務器了,但這個委托到底是如何完成的呢?這也是本章的看點之一。
生成HTTP請求消息
探索之旅從輸入網址開始
URL的各種格式
盡管URL有各種不同的寫法,但它們有一個共同點,那就是URL開頭的文字,即
http:
, ftp:
, file:
, mailto:
,這部分文字表示瀏覽器應當使用的訪問方法。我們可以把這部分理解為訪問時使用的協議類型。
瀏覽器先要解析URL
Web瀏覽器解析URL的過程
省略文件名的情況
http://www.lab.glasscom.com/dir/
對于上面這個URL我們可以這樣理解,以/
結尾代表/dir/
后面本來應該有的文件名被省略了。這種情況下會訪問事先設定好的默認文件,大多數情況下是index.html
或者default.htm
之類的文件名。因此,對于這個例子,服務器就會訪問/dir/index.html
或者/dir/default.htm
。
還有一些URL是像下面這樣只有Web服務器的域名的,這也是一種省略了文件名的形式。
http://www.lab.glasscom.com/
這個URL也是以/
結尾的,也就是說它表示訪問一個名叫/
的目錄,由于省略了文件名,所以結果就是訪問/index.html
或者/default.htm
這樣的文件。
那么,下面這個URL又是什么意思呢?
http://www.lab.glasscom.com
這次連結尾的/
都省略了。像這樣連目錄名都省略時,就會訪問根目錄下事先設置的默認文件。
但下面這個例子就更詭異了
http://www.lab.glasscom.com/whatisthis
這種情況一般會按照下面的慣例進行處理:如果Web服務器上存在名為whatisthis
的文件,則將whatisthis
作為文件名來處理;如果存在whatisthis
的目錄,則將whatisthis
作為目錄名來處理。因為我們無法創(chuàng)建兩個名字相同的文件和目錄。
瀏覽器的第一步工作就是對URL進行解析。
HTTP的基本思路
HTTP請求消息中包含的內容是對什么和進行怎樣的操作兩個部分,其中“對什么”的部分稱為
URI
。一般來說,URI的內容是一個存放網頁數據的文件名或者是一個CGI程序。“進行怎樣的操作”的部分稱為方法,方法表示需要讓Web服務器完成怎樣的工作,常用方法包括GET
和POST
等。除此之外,HTTP消息中還有一些用來表示附加信息的頭字段。客戶端向Web服務器發(fā)送數據時,會先發(fā)送頭字段,然后再發(fā)送數據。
在收到請求消息后,Web服務器會根據這些要求來完成自己的工作,然后將結果存放在響應消息中。在響應消息的開頭有一個狀態(tài)碼,狀態(tài)碼后面就是頭字段和網頁數據。
響應消息會被發(fā)送回客戶端,客戶端收到之后,瀏覽器會從消息中讀出所需的數據并顯示在屏幕上。到這里,HTTP的整個工作就完成了。
生成HTTP請求消息
對URL進行解析之后,瀏覽器確定了Web服務器和文件名,接下來就是根據這些信息來生成HTTP請求消息了。
HTTP消息的格式
一條請求消息中只能寫一個URI。如果需要獲取多個文件,必須對每個文件單獨發(fā)送1條請求。
向DNS服務器查詢Web服務器的IP地址
Socket庫提供查詢IP地址的功能
我們的計算機上有相應的DNS客戶端,而相當于DNS客戶端的部分稱為DNS解析器。通過DNS查詢IP地址的操作稱為域名解析。
解析器實際上是一段程序,它包含在操作系統的Socket庫中。Socket庫包含的程序組件可以讓其他的應用程序調用操作系統的網絡功能,而解析器就是這個庫中的其中一種程序組件。
Socket庫是用于調用網絡功能的程序組件集合。
通過解析器向DNS服務器發(fā)出查詢
解析器的用法非常簡單,只要像下圖一樣寫上解析器的程序名稱gethostbyname
以及Web服務器的域名www.lab.glasscom.com
就可以了,這樣就完成了對解析器的調用。
調用解析器后,解析器會向DNS服務器發(fā)送查詢消息,然后DNS服務器會返回響應消息。響應消息中包含查詢到的IP地址,解析器會取出IP地址,并將其寫入瀏覽器指定的內存地址中。
根據域名查詢IP地址時,瀏覽器會使用Socket庫中的解析器。
解析器的內部原理
當瀏覽器調用解析器時,程序的控制流程就會轉移到解析器的內部。解析器會根據DNS的規(guī)格,生成一條表示“請告訴我www.lab.glasscom.com的IP地址”的數據,并將它發(fā)送給DNS服務器。
HTTP消息是用文本編寫的,但DNS消息是使用二進制數據編寫的。
發(fā)送消息這個操作不是由解析器本身來執(zhí)行,而是要委托給操作系統內部的協議棧來執(zhí)行。這是因為和瀏覽器一樣,解析器本身也不具備使用網絡收發(fā)數據的功能。解析器調用協議棧后,控制流程會再次轉移,協議棧會執(zhí)行發(fā)送消息的操作,然后通過網卡將消息發(fā)送給DNS服務器。
向DNS服務器發(fā)送消息時,我們當然也需要知道DNS服務器的IP地址。只不過這個IP地址是作為TCP/IP的一個設置項目事先設置好的,不需要再去查詢了。Windows的設置如下圖所示。
全世界DNS服務器的大接力
DNS服務器的基本工作
DNS服務器的基本工作就是接收來自客戶端的查詢消息,然后根據消息的內容返回響應。來自客戶端的查詢消息包含以下三種信息:域名、Class以及記錄類型。
DNS服務器上事先保存有前面這三種信息對應的記錄數據,DNS服務器就是根據這些記錄查找符合查詢請求的內容并對客戶端作出相應的。
查詢IP地址時我們使用
A
這個記錄類型,查詢郵件服務器時我們要使用MX
類型。當記錄類型為MX
時,DNS服務器會在記錄中保存兩種信息,分別是郵件服務器的域名和優(yōu)先級。
DNS服務器會從域名與IP地址的對照表中查找相應的記錄,并返回IP地址。
域名的層次結構
互聯網中存在著不計其數的服務器,將這些服務器的信息全部保存在一臺DNS服務器中是不可能的,因此一定會出現DNS服務器中找不到要查詢的信息的情況。
直接說答案的話很簡單,就是將信息分布保存在多臺DNS服務器中,這些DNS服務器相互接力配合,從而查找出要查詢的信息。但這個機制其實有點復雜。
首先,DNS服務器中的所有信息都是按照域名以分層次的結構來保存的。
一個域的信息是作為一個整體存放在DNS服務器中的,不能將一個域拆開來存放在多臺DNS服務器中。但一臺DNS服務器中可以存放多個域的信息。
尋找相應的DNS服務器并獲取IP地址
這里的關鍵在于如何找到我們要訪問的Web服務器的信息歸哪一臺DNS服務器管。我們可以采用下面發(fā)的方法。
將負責管理下級域的DNS服務器的IP地址注冊到它們的上級DNS服務器中,然后上級DNS服務器的IP地址再注冊到更上一級的DNS服務器中。也就是說,負責管理lab.glasscom.com
這個域的DNS服務器的IP地址需要注冊到glasscom.com
的DNS服務器中,而glasscom.com
域的DNS服務器的IP地址又需要注冊到com
域的DNS服務器中。
這樣,我們就可以通過上級DNS服務器查詢出下級DNS服務器的IP地址,也就可以向下級DNS服務器發(fā)送查詢請求了。
在互聯網中,com
和jp
這類域名上面還有一級域
,稱為根域。根域在一般書寫域名時經常被忽略,一般在域名的最后加上一個句號www.lab.glasscom.com.
,這個.
就代表根域。根域的服務器中保管者com
、jp
的DNS服務器的信息。由于上級DNS服務器保管者所有下級DNS服務器的信息,所以我們可以從根域開始一路往下找到任意一個域的DNS服務器。除此之外,我們還需要將根域的DNS服務器信息保存在互聯網中所有的DNS服務器中。分配給根域DNS服務器的IP地址在全世界僅有13個,而且這些地址幾乎不發(fā)生變化,因此將這些地址保存在所有的DNS服務器中也不是一件難事。
我們用一張圖來看一下這個過程是如何進行的。
通過緩存加快DNS服務器的響應
有時候并不需要從最上級的根域開始查找,因為DNS服務器有一個緩存功能,可以記住之前查詢過的域名。如果要查詢的域名和相關信息已經在緩存中,那么就可以直接返回響應,緩存可以減少查詢所需的時間。
由于信息被緩存后,原本的注冊信息可能會發(fā)生改變,這時緩存中的信息就有可能是不正確的。因此,DNS服務器中保存的信息都設置一個有效期,當緩存中的信息超過有效期后,數據就會從緩存中被刪除。
委托協議棧發(fā)送消息
數據收發(fā)操作概覽
知道了IP地址之后,就可以委托操作系統內部的協議棧向這個目標IP地址,也就是我們要訪問的Web服務器發(fā)送消息了。
和向DNS服務器查詢IP地址的操作一樣,這里也需要使用Socket庫中的程序組件。不過,查詢IP地址只需要調用一個程序組件就可以了,而這里需要按照指定的順序調用多個程序組件。
向操作系統內部的協議棧發(fā)出委托時,需要按照指定的順序來調用Socket庫中的程序組件。
使用Socket庫來收發(fā)數據的操作過程如下圖所示。簡單來說,收發(fā)數據的兩臺計算機之間連接了一條數據通道,數據沿著這條通道流動,最終到達目的地。
這條管道并不是一開始就有的,在進行收發(fā)數據操作之前,雙方需要先建立起這條管道。建立管道的關鍵在于管道兩端的數據出入口,這些出入口稱為套接字。我們需要先創(chuàng)建套接字,然后再將套接字連接起來形成管道。服務器程序一般會在啟動后就創(chuàng)建好套接字并等待客戶端連接管道。管道在連接時是由客戶端發(fā)起的,但在斷開時可以由客戶端或服務器任意一方發(fā)起。其中一方斷開后,另一方也會隨之斷開,當管道斷開后,套接字也會被刪除。到此為止,通信操作就結束了。
綜上所述,收發(fā)數據的操作分為若干個階段,可以大致總結為以下4個。
- 創(chuàng)建套接字(創(chuàng)建套接字階段)
- 將管道連接到服務器端的套接字上(連接階段)
- 收發(fā)數據(通信階段)
- 斷開管道并刪除套接字(斷開階段)
在每個階段,Socket庫中的程序組件都會被調用來執(zhí)行相關的數據收發(fā)操作。這四個操作都是由操作系統中的協議棧來執(zhí)行的,是由瀏覽器委托。這些委托操作都是通過調用Socket庫中的程序組件來執(zhí)行的,但這些數據通信用的程序組件其實僅僅充當了一個橋梁的作用,并不執(zhí)行任何實質性的操作,應用程序的委托內容最終會被原原本本地傳遞給協議棧。因此后文會將Socket庫和協議棧看成一個整體來講解。
當調用Socket庫中的程序組件時,應用程序所指定的參數會通過Socket庫的程序組件傳遞給協議棧,并由協議棧來實際執(zhí)行相應的操作。
下面我們來探索一下應用程序(瀏覽器)委托收發(fā)數據的過程。這個過程的關鍵點是按照一定的順序調用Socket庫中的若干個程序組件。
創(chuàng)建套接字階段
客戶端創(chuàng)建套接字的操作非常簡單,只要調用Socket庫中的socket
程序組件就可以了。和調用解析器一樣,調用socket
之后,控制流程會轉移到socket內部并執(zhí)行創(chuàng)建套接字的操作。
套接字創(chuàng)建完成后,協議棧會返回一個描述符,應用程序會將收到的描述符放在內存中。描述符是用來識別不同的套接字的,因為實際上計算機中會同時進行多個數據的通信操作,有多個數據收發(fā)操作在進行,也就需要創(chuàng)建多個不同的套接字,描述符就是幫助我們來識別出特定的套接字的方法。
應用程序是通過“描述符”這一類似號碼牌的東西來識別套接字的。
連接階段:把管道接上去
接下來我們需要委托協議棧將客戶端創(chuàng)建的套接字與服務器那邊的套接字連接起來。應用程序通過調用Socket庫中的名為connect
的程序組件來完成這一操作。當調用connect時,需要指定描述符、服務器IP地址和端口號這三個參數。
這里解釋一下端口號,通過IP地址我們能識別出網絡上的某臺計算機。但連接操作的對象是某個具體的套接字,而通過端口號我們可以明確識別出某臺具體的計算機上的某個具體的套接字。
如果說描述符是用來在一臺計算機內部識別套接字的機制,那么端口號就是用來讓通信的另一方能夠識別出套接字的機制。
但網址上好像并沒有端口號?也不能像IP地址一樣去問DNS服務器,那怎么辦?
其實服務器上所使用的端口號是根據應用的種類實現規(guī)定好的,比如Web是80號端口,電子郵件是25號端口。只要制定了實現規(guī)定好的端口號,就可以鏈接到相應的服務器程序的套接字。
而客戶端自己在創(chuàng)建套接字時,協議棧會為這個套接字隨便分配一個端口號。接下來,當協議棧執(zhí)行連接操作時,會將這個隨便分配的端口號通知給服務器。
總結:
當調用connect后,協議棧就會執(zhí)行連接操作。當連接成功后,協議棧會將對方的IP地址和端口號等信息保存在套接字中,這樣就可以開始收發(fā)數據了。
描述符:應用程序用來識別套接字的機制
IP地址和端口號:客戶端和服務器之間用來識別對方套接字的機制
通信階段:傳遞消息
只要將數據送入套接字,數據就會被發(fā)送到對方的套接字中。這個操作需要Socket庫中的wirte
程序組件來完成。應用程序需要在內存中準備好要發(fā)送的數據,在這個具體例子里,根據用戶輸入的網址生成的HTTP請求消息就是我們要發(fā)送的數據。
當消息返回后,需要執(zhí)行的是接收消息的操作。接收消息的操作是通過Socket庫中的read
程序組件委托協議棧來完成的。調用read時需要制定用于存放接收到的響應消息的內存地址,這一內存地址稱為接收緩沖區(qū)。
斷開階段:收發(fā)數據結束
當瀏覽器收到數據之后,收發(fā)數據的過程就結束了。我們需要調用Socket庫的close
程序組件進入斷開階段。最終,連接在套接字之間的管道會被斷開,套接字本身也會被刪除。
斷開的過程如下。Web使用的HTTP協議規(guī)定,當Web服務器發(fā)送完響應消息之后,應該主動執(zhí)行斷開操作。
這就是HTTP的工作過程,HTTP協議將HTML文檔和圖片都作為單獨的對象來處理。因此,如果一個網頁中包含很多張圖片,就必須重復進行很多次連接、收發(fā)數據、斷開的操作。重復連接和斷開顯然是效率很低的。因此后來人們又設計出了能夠在一次連接中收發(fā)多個請求和相應的方法,在HTTP版本1.1中就可以使用這種方法。在這種情況下,當所有數據都請求完成后,瀏覽器會主動觸發(fā)斷開連接的操作。
專業(yè)名詞
URL
- Uniform Resource Locator,統一資源定位符
FTP
- File Transfer Protocol,文件傳送協議
- 這是一種在上傳、下載文件時使用的協議。使用FTP協議來傳送文件的程序也被叫做FTP。
HTTP
- Hypertext Transfer Protocol,超文本傳送協議
URI
- Uniform Resource Identifier,統一資源標識符
DNS
- Domain Name System,域名服務系統
- 將服務器名稱和IP地址進行關聯是DNS最常見的用法。但DNS的功能并不僅限于此,它還可以將郵件地址和郵件服務器進行關聯,以及為各種信息關聯相應的名稱
socket
表示程序組件的名稱
Socket庫
- Socket庫是用于調用網絡功能的程序組件集合
套接字
- 表示管道兩端的接口
協議棧
- 操作系統內部的網絡控制軟件,也叫“協議驅動”“TCP/IP驅動”等。
郵件服務器優(yōu)先級
- 當一個郵件地址對應多個郵件服務器時,需要根據優(yōu)先級來判斷哪個郵件服務器是優(yōu)先的。優(yōu)先級數值較小的郵件服務器代表更優(yōu)先
UDP
- User Datagram Protocol,用戶數據報協議