這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
我們平時瀏覽網頁的時候,會開啟瀏覽器,輸入網址後按下斷行符號鍵,然後就會顯示出你想要瀏覽的內容。在這個看似簡單的使用者行為背後,到底隱藏了些什麼呢?
對於普通的上網過程,系統其實是這樣做的:瀏覽器本身是一個用戶端,當你輸入URL的時候,首先瀏覽器會去請求DNS伺服器,通過DNS擷取相應的網域名稱對應的IP,然後通過IP地址找到IP對應的伺服器後,要求建立TCP串連,等瀏覽器發送完HTTP Request(請求)包後,伺服器接收到請求包之後才開始處理請求包,伺服器調用自身服務,返回HTTP Response(響應)包;用戶端收到來自伺服器的響應後開始渲染這個Response包裡的主體(body),等收到全部的內容隨後斷開與該伺服器之間的TCP串連。
圖3.1 使用者訪問一個Web網站的過程
一個Web伺服器也被稱為HTTP伺服器,它通過HTTP協議與用戶端通訊。這個用戶端通常指的是Web瀏覽器(其實手機端用戶端內部也是瀏覽器實現的)。
Web伺服器的工作原理可以簡單地歸納為:
- 客戶機通過TCP/IP協議建立到伺服器的TCP串連
- 用戶端向伺服器發送HTTP協議請求包,請求伺服器裡的資來源文件
- 伺服器向客戶機發送HTTP協議應答包,如果請求的資源套件含有動態語言的內容,那麼伺服器會調用動態語言的解釋引擎負責處理“動態內容”,並將處理得到的資料返回給用戶端
- 客戶機與伺服器斷開。由用戶端解釋HTML文檔,在用戶端螢幕上渲染圖形結果
一個簡單的HTTP事務就是這樣實現的,看起來很複雜,原理其實是挺簡單的。需要注意的是客戶機與伺服器之間的通訊是非持久串連的,也就是當伺服器發送了應答後就與客戶機中斷連線,等待下一次請求。
URL和DNS解析
我們瀏覽網頁都是通過URL訪問的,那麼URL到底是怎麼樣的呢?
URL(Uniform Resource Locator)是“統一資源定位器”的英文縮寫,用於描述一個網路上的資源, 基本格式如下
scheme://host[:port#]/path/.../[?query-string][#anchor]scheme 指定底層使用的協議(例如:http, https, ftp)host HTTP伺服器的IP地址或者網域名稱port# HTTP伺服器的預設連接埠是80,這種情況下連接埠號碼可以省略。如果使用了別的連接埠,必須指明,例如 http://www.cnblogs.com:8080/path 訪問資源的路徑query-string 發送給http伺服器的資料anchor 錨
DNS(Domain Name System)是“網域名稱系統”的英文縮寫,是一種組織成域階層的電腦和網路服務命名系統,它用於TCP/IP網路,它從事將主機名稱或網域名稱轉換為實際IP地址的工作。DNS就是這樣的一位“翻譯官”,它的基本工作原理可用來表示。
圖3.2 DNS工作原理
更詳細的DNS解析的過程如下,這個過程有助於我們理解DNS的工作模式
在瀏覽器中輸入www.qq.com網域名稱,作業系統會先檢查自己本地的hosts檔案是否有這個網址映射關係,如果有,就先調用這個IP地址映射,完成網域名稱解析。
如果hosts裡沒有這個網域名稱的映射,則尋找本地DNS解析器緩衝,是否有這個網址映射關係,如果有,直接返回,完成網域名稱解析。
如果hosts與本地DNS解析器緩衝都沒有相應的網址映射關係,首先會找TCP/IP參數中設定的首選DNS伺服器,在此我們叫它本地DNS伺服器,此伺服器收到查詢時,如果要查詢的網域名稱,包含在本地配置地區資源中,則返回解析結果給客戶機,完成網域名稱解析,此解析具有權威性。
如果要查詢的網域名稱,不由本地DNS伺服器地區解析,但該伺服器已緩衝了此網址映射關係,則調用這個IP地址映射,完成網域名稱解析,此解析不具有權威性。
如果本地DNS伺服器本地地區檔案與緩衝解析都失效,則根據本地DNS伺服器的設定(是否設定轉寄站)進行查詢,如果未用轉寄模式,本地DNS就把請求發至 “根DNS伺服器”,“根DNS伺服器”收到請求後會判斷這個網域名稱(.com)是誰來授權管理,並會返回一個負責該頂級網域名稱伺服器的一個IP。本地DNS伺服器收到IP資訊後,將會聯絡負責.com域的這台伺服器。這台負責.com域的伺服器收到請求後,如果自己無法解析,它就會找一個管理.com域的下一級DNS伺服器位址(qq.com)給本地DNS伺服器。當本地DNS伺服器收到這個地址後,就會找qq.com網域服務器,重複上面的動作,進行查詢,直至找到www.qq.com主機。
如果用的是轉寄模式,此DNS伺服器就會把請求轉寄至上一級DNS伺服器,由上一級伺服器進行解析,上一級伺服器如果不能解析,或找根DNS或把轉請求轉至上上級,以此迴圈。不管是本地DNS伺服器用是是轉寄,還是根提示,最後都是把結果返回給本地DNS伺服器,由此DNS伺服器再返回給客戶機。
圖3.3 DNS解析的整個流程
所謂 遞迴查詢過程
就是 “查詢的遞交者” 更替, 而 迭代查詢過程
則是 “查詢的遞交者”不變。
舉個例子來說,你想知道某個一起上法律課的女孩的電話,並且你偷偷拍了她的照片,回到寢室告訴一個很仗義的哥們兒,這個哥們兒二話沒說,拍著胸脯告訴你,甭急,我替你查(此處完成了一次遞迴查詢,即,問詢者的角色更替)。然後他拿著照片問了學院大四學長,學長告訴他,這姑娘是xx系的;然後這哥們兒馬不停蹄又問了xx系的辦公室主任助理同學,助理同學說是xx系yy班的,然後很仗義的哥們兒去xx系yy班的班長那裡取到了該女孩兒電話。(此處完成若干次迭代查詢,即,問詢者角色不變,但反覆更替問詢對象)最後,他把號碼交到了你手裡。完成整個查詢過程。
通過上面的步驟,我們最後擷取的是IP地址,也就是瀏覽器最後發起請求的時候是基於IP來和伺服器做資訊互動的。
HTTP協議詳解
HTTP協議是Web工作的核心,所以要瞭解清楚Web的工作方式就需要詳細的瞭解清楚HTTP是怎麼樣工作的。
HTTP是一種讓Web伺服器與瀏覽器(用戶端)通過Internet發送與接收資料的協議,它建立在TCP協議之上,一般採用TCP的80連接埠。它是一個請求、響應協議--用戶端發出一個請求,伺服器響應這個請求。在HTTP中,用戶端總是通過建立一個串連與發送一個HTTP請求來發起一個事務。伺服器不能主動去與用戶端聯絡,也不能給用戶端發出一個回調串連。用戶端與伺服器端都可以提前中斷一個串連。例如,當瀏覽器下載一個檔案時,你可以通過點擊“停止”鍵來中斷檔案的下載,關閉與伺服器的HTTP串連。
HTTP協議是無狀態的,同一個用戶端的這次請求和上次請求是沒有對應關係,對HTTP伺服器來說,它並不知道這兩個請求是否來自同一個用戶端。為瞭解決這個問題, Web程式引入了Cookie機制來維護串連的可持續狀態。
HTTP協議是建立在TCP協議之上的,因此TCP攻擊一樣會影響HTTP的通訊,例如比較常見的一些攻擊:SYN Flood是當前最流行的DoS(拒絕服務的攻擊)與DdoS(分散式阻斷服務攻擊)的方式之一,這是一種利用TCP協議缺陷,發送大量偽造的TCP串連請求,從而使得被攻擊方資源耗盡(CPU滿負荷或記憶體不足)的攻擊方式。
HTTP請求包(瀏覽器資訊)
我們先來看看Request包的結構, Request包分為3部分,第一部分叫Request line(請求行), 第二部分叫Request header(要求標頭),第三部分是body(主體)。header和body之間有個空行,請求包的例子所示:
GET /domains/example/ HTTP/1.1 //請求行: 要求方法 請求URI HTTP協議/協議版本Host:www.iana.org //服務端的主機名稱User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //瀏覽器資訊Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //用戶端能接收的mineAccept-Encoding:gzip,deflate,sdch //是否支援流壓縮Accept-Charset:UTF-8,*;q=0.5 //用戶端字元編碼集//空行,用於分割要求標頭和訊息體//訊息體,請求資源參數,例如POST傳遞的參數
HTTP協議定義了很多與伺服器互動的要求方法,最基本的有4種,分別是GET,POST,PUT,DELETE。一個URL地址用於描述一個網路上的資源,而HTTP中的GET, POST, PUT, DELETE就對應著對這個資源的查,改,增,刪4個操作。我們最常見的就是GET和POST了。GET一般用於擷取/查詢資源資訊,而POST一般用於更新資源資訊。
通過fiddler抓包可以看到如下請求資訊:
通過fiddler抓包可以看到如下請求資訊:
圖3.4 fiddler抓取的GET資訊
圖3.5 fiddler抓取的POST資訊
我們看看GET和POST的區別:
- 我們可以看到GET請求訊息體為空白,POST請求帶有訊息體。
- GET提交的資料會放在URL之後,以
?
分割URL和傳輸資料,參數之間以&
相連,如EditPosts.aspx?name=test1&id=123456
。POST方法是把提交的資料放在HTTP包的body中。
- GET提交的資料大小有限制(因為瀏覽器對URL的長度有限制),而POST方法提交的資料沒有限制。
- GET方式提交資料,會帶來安全問題,比如一個登入頁面,通過GET方式提交資料時,使用者名稱和密碼將出現在URL上,如果頁面可以被緩衝或者其他人可以訪問這台機器,就可以從記錄獲得該使用者的帳號和密碼。
HTTP響應包(伺服器資訊)
我們再來看看HTTP的response包,他的結構如下:
HTTP/1.1 200 OK //狀態行Server: nginx/1.0.8 //伺服器使用的WEB軟體名及版本Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //發送時間Content-Type: text/html //伺服器發送資訊的類型Transfer-Encoding: chunked //表示發送HTTP包是分段發的Connection: keep-alive //保持串連狀態Content-Length: 90 //主體內容長度//空行 用來分割訊息頭和主體<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //訊息體
Response包中的第一行叫做狀態行,由HTTP協議版本號碼, 狀態代碼, 狀態訊息 三部分組成。
狀態代碼用來告訴HTTP用戶端,HTTP伺服器是否產生了預期的Response。HTTP/1.1協議中定義了5類狀態代碼, 狀態代碼由三位元字組成,第一個數字定義了響應的類別
- 1XX 提示資訊 - 表示請求已被成功接收,繼續處理
- 2XX 成功 - 表示請求已被成功接收,理解,接受
- 3XX 重新導向 - 要完成請求必須進行更進一步的處理
- 4XX 用戶端錯誤 - 請求有語法錯誤或請求無法實現
- 5XX 伺服器端錯誤 - 伺服器未能實現合法的請求
我們看下面這個圖展示了詳細的返回資訊,左邊可以看到有很多的資源返回碼,200是常用的,表示正常資訊,302表示跳轉。response header裡面展示了詳細的資訊。
圖3.6 訪問一次網站的全部請求資訊
HTTP協議是無狀態的和Connection: keep-alive的區別
無狀態是指協議對於交易處理沒有記憶能力,伺服器不知道用戶端是什麼狀態。從另一方面講,開啟一個伺服器上的網頁和你之前開啟這個伺服器上的網頁之間沒有任何聯絡。
HTTP是一個無狀態的連線導向的協議,無狀態不代表HTTP不能保持TCP串連,更不能代表HTTP使用的是UDP協議(面對無串連)。
從HTTP/1.1起,預設都開啟了Keep-Alive保持串連特性,簡單地說,當一個網頁開啟完成後,用戶端和伺服器之間用於傳輸HTTP資料的TCP串連不會關閉,如果用戶端再次訪問這個伺服器上的網頁,會繼續使用這一條已經建立的TCP串連。
Keep-Alive不會永久保持串連,它有一個保持時間,可以在不同伺服器軟體(如Apache)中設定這個時間。
請求執行個體
圖3.7 一次請求的request和response
上面這張圖我們可以瞭解到整個的通訊過程,同時細心的讀者是否注意到了一點,一個URL請求但是左側邊欄裡面為什麼會有那麼多的資源請求(這些都是靜態檔案,go對於靜態檔案有專門的處理方式)。
這個就是瀏覽器的一個功能,第一次請求url,伺服器端返回的是html頁面,然後瀏覽器開始渲染HTML:當解析到HTML DOM裡面的圖片串連,css指令碼和js指令碼的連結,瀏覽器就會自動發起一個請求靜態資源的HTTP請求,擷取相對應的靜態資源,然後瀏覽器就會渲染出來,最終將所有資源整合、渲染,完整展現在我們面前的螢幕上。
網頁最佳化方面有一項措施是減少HTTP請求次數,就是把盡量多的css和js資源合并在一起,目的是盡量減少網頁請求靜態資源的次數,提高網頁載入速度,同時減緩伺服器的壓力。
原文連結:https://astaxie.gitbooks.io/build-web-application-with-golang/content/zh/03.1.html