前言
這篇文章主要是我平時在學習HTTP過程中看到的一些知識點,現在把他們總結成一篇文章,建立一個自己的知識體系,全是乾貨。另外,推薦一本非常棒的HTTP書——《圖解HTTP》,這本書圖文並茂,挺有趣的。
HTTP是基於TCP/IP協議的應用層協議,用於客戶端和伺服器之間的通訊,預設80埠。我們按照他的發展歷程的時間順序開始說。
1. HTTP/0.9
1990年提出的,是最早期的版本,只有一個命令GET。
2. HTTP/1.0
1996年5月提出的。
缺點
:每個TCP連線只能傳送一個請求。解決方法
:Connection:keep-alive
3. HTTP/1.1
1997年1月提出,現在使用最廣泛的。
3.1 特性
-
長連線
:TCP連線預設不關閉,可以被多個請求複用。對於同一個域名,大多數瀏覽器允許同時建立6個持久連線。預設開啟Connection:keep-alive。 -
管道機制
:在同一個TCP連線裡,可以同時傳送多個請求。但是伺服器還是要按照請求的順序進行響應,會造成“隊頭阻塞”。
3.2 HTTP首部
HTTP首部分為請求報文和響應報文。它們的格式如下所示:
- 請求報文:
- 響應報文:
其中首部欄位又分為很多種,我們先看通用首部欄位,這是請求報文和響應報文種都會使用的首部。
3.2.1 通用首部欄位
1、Cache-Control
:操作快取的工作機制
引數:
- public:明確表明其他使用者也可以利用快取
- private:快取只給特定的使用者
- no-cache:客戶端傳送這個指令,表示客戶端不接收快取過的響應,必須到伺服器取;伺服器返回這個指令,指快取伺服器不能對資源進行快取。其實是不快取過期資源,要向伺服器進行有效期確認後再處理資源。
- no-store:指不進行快取
- max-age:快取的有效時間(相對時間)
2、Connection
:
- Connection:keep-Alive (持久連線)
- Connection:不再轉發的首部欄位名
3、Date
:表明建立http報文的日期和時間
4、Pragma
:相容http1.0,與Cache-Control:no-cache含義一樣。但只用在客戶端傳送的請求中,告訴所有的中間伺服器不返回快取。形式唯一:Pragma:no-cache
5、Trailer
:會事先說明在報文主體後記錄了哪些首部欄位,該首部欄位可以應用在http1.1版本分塊傳輸編碼中。
6、Transfer-Encoding
:chunked (分塊傳輸編碼),
規定傳輸報文主體時採用的編碼方式,http1.1的傳輸編碼方式只對分塊傳輸編碼有效
7、Upgrade
:升級一個成其他的協議,需要額外指定Connection:Upgrade。伺服器可用101狀態碼作為相應返回。
8、Via
:追蹤客戶端和伺服器之間的請求和響應報文的傳輸路徑。可以避免請求迴環發生,所以在經過代理時必須要附加這個欄位。
3.2.2 請求首部欄位
1、Accept:通知伺服器,使用者代理能夠處理的媒體型別及媒體型別的相對優先順序 q表示優先順序的權重值,預設為q = 1.0,範圍是0~1(可精確到小數點後3位,1為最大值) 當伺服器提供多種內容時,會先返回權重值最高的媒體型別
2、Accept-Charset:支援的字符集及字符集的相對優先順序,跟Accept一樣,用q來表示相對優先順序。這個欄位應用於內容協商機制的伺服器驅動協商。
3、Accept-Encoding:支援的內容編碼及內容編碼的優先順序順序,q表示相對優先順序。 內容編碼:gzip、compress、deflate、identity(不執行壓縮或者不會變化的預設編碼格式)。 可以使用*作為萬用字元,指定任意的編碼格式。
4、Accept-Language:能夠處理的自然語言集,以及相對優先順序。
3.3 狀態碼
101 協議升級
,主要用於升級到websocket,也可以用於http2
200 OK
204 No content
,伺服器成功處理請求,但是返回的響應報文中不含實體的主體部分
206 Partial Content
,表示客戶端像伺服器進行了範圍請求(Content-Range欄位),伺服器成功返回指定範圍的實體內容
301 永久性重定向
,表示請求的資源已經被分配了新的url,舊地址以後都不能再訪問了,伺服器會返回location欄位,包含的是新的地址。
302 臨時性重定向
,表示請求的資源臨時移動到一個新地址
注意:儘量使用301跳轉,因為302會造成網址劫持,可能被搜尋引擎判為可疑轉向,甚至認為是作弊。
原因:從網站A(網站比較爛)上做了一個302跳轉到網站B(搜尋排名很靠前),這時候有時搜尋引擎會使用網站B的內容,但卻收錄了網站A的地址,這樣在不知不覺間,網站B在為網站A作貢獻,網站A的排名就靠前了。
303 See Other
,與302功能相同,但是它明確規定客戶端應採用GET方法獲取資源
304 未修改
,協商快取中返回的狀態碼
307 臨時重定向
,與302功能相同,但規定不能從POST變成GET
當301、302、303響應狀態碼返回時,幾乎所有瀏覽器都會把post改成get,並刪除請求報文內的主體,之後請求會自動再次傳送。然而301、302標準是禁止將post方法改變成get方法的,但實際使用時大家都會這麼做。所以需要307。
400 Bad Request
,表示請求報文中存在語法錯誤。當錯誤發生時,需要修改請求的內容再次傳送請求
401 unauthorized
,表示傳送的請求需要有通過HTTP認證(BASIC認證、DIGEST認證)的認證資訊。如果之前已經進行過一次請求,表示使用者認證失敗。
403 禁止
,表示拒絕對請求資源的訪問
404 Not Found
,表明伺服器上無法找到請求的資源
500 Internet Server Error
,該狀態碼錶示伺服器在執行請求時發生了錯誤
500 Service Unavailable
,表示伺服器暫時處於超負荷或者處於停機維護狀態,現在無法處理請求
4. SPDY協議
2009年穀歌提出。
SPDY結構
:
新增特性
:
(1)多路複用:通過一個TCP連線,可以無限制處理多個HTTP請求。
(2)賦予請求優先順序:給請求逐個分配優先順序順序。可以解決在傳送多個請求時,因頻寬低而導致響應變慢的問題。
(3)壓縮HTTP首部:壓縮方式:DELEFT
(4)推送功能
(5)伺服器提示功能:伺服器可以主動提示客戶端請求所需的資源。缺點
:
SPDY強制使用https。而且SPDY基本上只是將單個域名下的通訊多路複用,所以當一個web網站上使用多個域名下的資源時,改善效果就會受到限制。
5. WebSocket
html5新提出來的,是web瀏覽器與web伺服器之間的全雙工通訊標準。主要是為了解決ajax和comet裡的xmlhttprequest附帶的缺陷所引起的問題。
5.1 特性
(1)推送功能:伺服器可直接傳送資料,不需要等待客戶端的請求;
(2)基於TCP傳輸協議,並複用HTTP的握手通道;
(3)支援雙向通訊,用於實時傳輸訊息;
(4)更好的二進位制支援;
(5)更靈活,更高效。
5.2 建立連線過程
1、客戶端:發起協議升級請求
GET / HTTP/1.1 `採用HTTP報文格式,只支援get請求`
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade `表示要升級協議`
Upgrade: websocket `表示升級到websocket協議`
Sec-WebSocket-Version: 13 `表示websocket 的版本`
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw== `是一個 Base64 encode 的值,是瀏覽器隨機生成的`
Sec-WebSocket-Protocol:chat, superchat `用來指定一個特定的子協議,一旦這個欄位有設定,那麼伺服器需要在建立連線的響應頭中包含同樣的欄位,內容就是選擇的子協議之一。`
複製程式碼
2、服務端:響應協議升級
HTTP/1.1 101 Switching Protocols `101表示協議切換==`
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU= `經過伺服器確認,並且加密過後的 Sec-WebSocket-Key`
Sec-WebSocket-Protocol:chat `表示最終使用的協議`
複製程式碼
Sec-WebSocket-Key 的加密過程為:
- 將Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
- 通過SHA1計算出摘要,並轉成base64字串。
3、雙方握手成功後,就是全雙工的通訊了,接下來就是用websocket協議來進行通訊了。
5.3 Ajax 輪詢、長輪詢、WebSocket原理解析
1、ajax輪詢
讓瀏覽器每隔一定的時間就傳送一次請求,詢問伺服器是否有新資訊。
2、長輪詢(Long Poll)
採用的阻塞模式。客戶端發起連線後,如果沒訊息,伺服器不會馬上告訴你沒訊息,而是將這個請求掛起(pending),直到有訊息才返回。返回完成或者客戶端主動斷開後,客戶端再次建立連線,周而復始。Comet就是採用的長輪詢。
3、websocket
WebSocket 是類似 Socket 的TCP長連線通訊模式。一旦 WebSocket 連線建立後,後續資料都以幀序列的形式傳輸。而且瀏覽器和伺服器就可以隨時主動傳送訊息給對方,是全雙工通訊。
優點:在海量併發及客戶端與伺服器互動負載流量大的情況下,極大的節省了網路頻寬資源的消耗,有明顯的效能優勢,且客戶端傳送和接受訊息是在同一個持久連線上發起,實時性優勢明顯。
6. HTTP2
2015年釋出,它是基於SPDY的,以下是它的一些新特性:
1、 二進位制分幀
:
http/1.x 是一個超文字協議,而 http2 是一個二進位制協議,被稱之為二進位制分幀。
二進位制格式在協議的解析和優化擴充套件上帶來更多的優勢和可能。
協議格式為幀,幀由 Frame Header(頭資訊幀)和 Frame Payload(資料幀)組成,如下所示:
- Length 欄位用來表示 Frame Payload 資料大小。
- Type 欄位用來表示該幀中的 Frame Payload 儲存的是 header 資料還是 body 資料。除了用於標識 header/body,還有一些額外的 Frame Type。
- Stream Identifier 用來標識該 frame 屬於哪個 stream。
- Frame Payload 用來儲存 header 或者 body 的資料。
2、頭部壓縮 HPACK
:
請求和響應首部壓縮,客戶端和服務端共同維護一張頭資訊表,所有欄位存入這個表,生成一個索引號,通過傳送索引號提高速度。HPACK壓縮會經過兩步:
- 傳輸的value,會經過一遍Huffman coding來節省資源;
- 為了server和client同步, 兩邊都需要保留一份Header list, 並且,每次傳送請求時,都會檢查更新。
3、服務端推送
:
服務端主動向客戶端推送資料。如果客戶端請求一個html檔案,服務端把html檔案返回給客戶端之後,還會相應的把html檔案中的js、css、圖片推送給客戶端。
4、多路複用
:
只需要建立一個TCP連線,瀏覽器和伺服器可以同時傳送多個請求或者回應,而且不需要按照順序一一對應,避免了“隊頭阻塞”。
5、資料流
:
當客戶端同時向服務端發起多個請求,那麼這些請求會被分解成一一個的幀,每個幀都會在一個 TCP 鏈路中無序的傳輸,同一個請求的幀的 Stream Identifier 都是一樣的。當幀到達服務端之後,就可以根據 Stream Identifier 來重新組合得到完整的請求。
並且規定:客戶端發出的資料流ID為奇數,伺服器發出的ID為偶數。Stream Identifier (資料流ID)就是用來標識該幀屬於哪個請求的。
7. HTTPS
HTTPS = HTTP+加密+認證+完整性保護
它的加密過程是:
- server生成一個公鑰和私鑰,把公鑰傳送給第三方認證機構(CA);
- CA把公鑰進行MD5加密,生成數字簽名;再把數字簽名用CA的私鑰進行加密,生成數字證照。CA會把這個數字證照返回給server;
- server拿到數字證照之後,就把它傳送給瀏覽器;
- 瀏覽器會對數字證照進行驗證,首先,瀏覽器本身會內建CA的公鑰,會用這個公鑰對數字證照解密,驗證是否是受信任的CA生成的數字證照;
- 驗證成功後,瀏覽器會隨機生成對稱祕鑰,用server的公鑰加密這個對稱祕鑰,再把加密的對稱祕鑰傳送給server;
- server收到對稱祕鑰,會用自己的私鑰進行解密,之後,它們之間的通訊就用這個對稱祕鑰進行加密,來維持通訊。
下圖是加密過程的圖解,可以對照著圖片理一遍。
8. HTTP快取機制
8.1 快取分類
HTTP的快取分為強快取和協商快取(對比快取)。
強制快取
在快取資料未失效的情況下,可以直接使用快取資料;在沒有快取資料的時候,瀏覽器向伺服器請求資料時,伺服器會將資料和快取規則一併返回,快取規則資訊包含在響應header中。
-
Expires:快取過期時間(HTTP1.0)
缺點:生成的是絕對時間,但是客戶端時間可以隨意修改,會導致誤差。
-
Cache-Control :HTTP1.1,優先順序高於Expires
可設定引數:
private: 客戶端可以快取
public: 客戶端和代理伺服器都可快取
max-age=xxx: 快取的內容將在 xxx 秒後失效
no-cache: 需要使用協商快取來驗證快取資料(後面介紹)
no-store: 所有內容都不會快取,強制快取,對比快取都不會觸發
Expires和Cache-Control決定了瀏覽器是否要傳送請求到伺服器,ETag和Last-Modified決定了伺服器是要返回304+空內容還是新的資原始檔。
協商快取
瀏覽器第一次請求資料時,伺服器會將快取標識與資料一起返回給客戶端,客戶端將二者備份至快取資料庫中。再次請求資料時,客戶端將備份的快取標識傳送給伺服器,伺服器根據快取標識進行判斷,判斷成功後,返回304狀態碼,通知客戶端比較成功,可以使用快取資料。
- Last-Modified / If-Modified-Since
Last-Modified
:伺服器在響應請求時,告訴瀏覽器資源的最後修改時間。If-Modified-Since
:再次請求伺服器時,通過此欄位通知伺服器上次請求時,伺服器返回的資源最後修改時間。
缺點:Last-Modified 標註的最後修改時間只能精確到秒,如果有些資源在一秒之內被多次修改的話,他就不能準確標註檔案的新鮮度了。如果某些資源會被定期生成,當內容沒有變化,但 Last-Modified 卻改變了,導致檔案沒使用快取有可能存在伺服器沒有準確獲取資源修改時間,或者與代理伺服器時間不一致的情形。
- Etag / If-None-Match(優先順序高於Last-Modified / If-Modified-Since)
Etag
:給資源計算得出的一個唯一標誌符。If-None-Match
:再次請求伺服器時,通過此欄位通知伺服器客戶端快取資料的唯一標識。
8.2 快取判斷順序
- 先判斷Cache-Control,在Cache-Control的max-age之內,直接返回200 from cache;
- 沒有Cache-Control再判斷Expires,再Expires之內,直接返回200 from cache;
- Cache-Control=no-cache或者不符合Expires,瀏覽器向伺服器傳送請求;
- 伺服器同時判斷ETag和Last-Modified,都一致,返回304,有任何一個不一致,返回200。
具體過程如下圖:
8.3 cookie、session
8.3.1 cookie
解決http的無狀態問題,是客戶端儲存使用者資訊的一種機制,用來記錄使用者的一些資訊,來實現session的跟蹤。
- cookie屬性
name、value
:以key/value的形式存在
comment
:說明該cookie的用處
domain
:可以訪問該cookie的域名
Expires/maxAge
:cookie失效時間。負數:臨時cookie,關閉瀏覽器就失效;0:表示刪除cookie,預設為-1
path
:可以訪問此cookie的頁面路徑
size
:cookie的大小
secure
:是否以https協議傳輸
version
:該cookie使用的版本號,0遵循Netscape規範,大多數用這種,1遵循W3C規範
HttpOnly
:此屬性為true,則只有在http請求頭中會帶有此cookie的資訊,而不能通過document.cookie來訪問此cookie,能防止XSS攻擊。
- cookie機制原理:
客戶端請求伺服器時,如果伺服器需要記錄該使用者狀態,就使用response向客戶端瀏覽器頒發一個Cookie。而客戶端瀏覽器會把Cookie儲存起來。當瀏覽器再請求伺服器時,瀏覽器把請求的網址連同該Cookie一同提交給伺服器。伺服器通過檢查該Cookie來獲取使用者狀態。
- cookie同源和跨域:
cookie的同源是域名相同,忽略協議和埠,不可跨域。
8.3.2 session
session是在服務端儲存的一個資料結構,用來跟蹤使用者的狀態,這個資料可以儲存在叢集、資料庫、檔案中。
-
session的執行依賴session id,而session id是存在cookie中的
-
session機制原理:
當客戶端請求建立一個session的時候,伺服器會先檢查這個客戶端的請求裡是否已包含了一個session標識——sessionId。如果已包含這個sessionId,則說明以前已經為此客戶端建立過session,伺服器就按照sessionId把這個session檢索出來使用(如果檢索不到,可能會新建一個。如果客戶端請求不包含sessionId,則為此客戶端建立一個session並且生成一個與此session相關聯的sessionId。
- 如果禁用cookie怎麼辦?
使用URL重寫技術來進行會話跟蹤。在 url 中傳遞 session id,即每次HTTP互動,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的引數,服務端據此來識別使用者。
8.3.3 區別
- cookie和session的區別
-
cookie 資料存放在客戶的瀏覽器上,session資料放在伺服器上;
-
cookie 不是很安全,別人可以分析存放在本地的 cookie 並進行 cookie 欺騙考慮到安全應當使用 session;
-
session 會在一定時間內儲存在伺服器上。當訪問增多,會比較佔用你伺服器的效能考慮到減輕伺服器效能方面,應當使用 cookie;
-
單個cookie儲存的資料不能超過4K,很多瀏覽器都限制一個站點最多儲存20個 cookie。
鑑於上述區別我們建議:
(1)將登陸資訊等重要資訊存放為 session;
(2)其他資訊如果需要保留,可以放在 cookie 中。
- localStorage,sessionStorage和cookie的區別
共同點:都是儲存在瀏覽器端、且同源的。
資料儲存方面
- cookie資料始終在同源的http請求中攜帶(即使不需要),即cookie在瀏覽器和伺服器間來回傳遞。cookie資料還有路徑(path)的概念,可以限制cookie只屬於某個路徑下。
- sessionStorage和localStorage不會自動把資料傳送給伺服器,僅在本地儲存。
儲存資料大小
- 儲存大小限制也不同,cookie資料不能超過4K,同時因為每次http請求都會攜帶cookie,所以cookie只適合儲存很小的資料,如會話標識。
- sessionStorage和localStorage雖然也有儲存大小的限制,但比cookie大得多,可以達到5M或更大。
資料儲存有效期
- sessionStorage:僅在當前瀏覽器視窗關閉之前有效;
- localStorage:始終有效,視窗或瀏覽器關閉也一直儲存,本地儲存,因此用作持久資料;
- cookie:只在設定的cookie過期時間之前有效,即使視窗關閉或瀏覽器關閉。
作用域不同
- sessionStorage不在不同的瀏覽器視窗中共享,即使是同一個頁面;
- localstorage在所有同源視窗中都是共享的;也就是說只要瀏覽器不關閉,資料仍然存在。
- cookie: 也是在所有同源視窗中都是共享的.也就是說只要瀏覽器不關閉,資料仍然存在。
9. 跨域
跨域產生的原因,是因為受到同源策略的限制。同源策略指的是協議、域名、埠不相同。這裡我將介紹三種跨域的方式:JSONP、CORS(跨域資源共享)、document.domain + iframe。
9.1 JSONP
1. 原理
動態插入script標籤(因為script標籤不受同源策略的限制),通過插入script標籤引入一個js檔案,這個js檔案載入成功之後會執行我們在url中指定的回撥函式,並且會把我們需要的json資料作為引數傳入。
2. 實現
(1)原生實現:
var script = document.createElement('script');
script.type = 'text/javascript';
// 傳參並指定回撥執行函式為onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回撥函式
function onBack(res) {
alert(JSON.stringify(res));
}
//服務端返回如下(返回時即執行全域性函式):
onBack({"status": true, "user": "admin"})
複製程式碼
(2)jquery ajax:
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 請求方式為jsonp
jsonpCallback: "onBack", // 自定義回撥函式名
data: {}
});
複製程式碼
9.2 CORS
1. 原理
伺服器在響應頭中設定相應的選項,瀏覽器如果支援這種方法的話就會將這種跨站資源請求視為合法,進而獲取資源。
2. 實現
CORS分為簡單請求和複雜請求,簡單請求指的是:
(1)請求方法是以下三種方法之一:HEAD、GET、POST;
(2)HTTP的頭資訊不超出以下幾種欄位:
Accept、Accept-Language、Content-Language、Last-Event-ID、
Content-Type(只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain)。
其他情況就是非簡單請求了。
- 簡單請求
(1)請求頭
:
Origin: http://www.domain.com
複製程式碼
(2)響應頭
:
Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Credentials: true `是否允許傳送cookie`
Access-Control-Expose-Headers: FooBar `CORS請求時,只能拿到6個基本欄位:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他欄位,就必須指定。`
複製程式碼
(3)另外,ajax請求中
,如果要傳送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名,還要設定以下內容:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
複製程式碼
- 非簡單請求
(1)預檢請求
:
OPTIONS /cors HTTP/1.1 `OPTIONS請求是用來詢問的`
Origin: http://www.domian.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
複製程式碼
(2)響應頭
:
Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Methods: GET, POST, PUT `伺服器支援的所有跨域請求的方法`
Access-Control-Allow-Headers: X-Custom-Header `伺服器支援的所有頭資訊欄位,不限於瀏覽器在"預檢"中請求的欄位。`
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000 `指定本次預檢請求的有效期,單位為秒`
複製程式碼
(3)之後的步驟就同簡單請求了
。
這是CORS的整個流程圖:
與JSOP的比較:
JSONP只支援GET請求,CORS支援所有型別的HTTP請求。JSONP的優勢在於支援老式瀏覽器,以及可以向不支援CORS的網站請求資料。
複製程式碼
9.3 document.domain + iframe
此方案僅限主域相同,子域不同的跨域應用場景。
1.原理
兩個頁面都通過js強制設定document.domain為基礎主域,就實現了同域。
2.實現
(1)父視窗:(www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
複製程式碼
(2)子視窗:(child.domain.com/b.html)
<script>
document.domain = 'domain.com';
// 獲取父視窗中變數
alert('get js data from parent ---> ' + window.parent.user);
</script>
複製程式碼