簡介
超文本傳輸協(xié)議(Hypertext Transfer Protocol娱仔,簡稱HTTP)是應(yīng)用層協(xié)議源祈。HTTP 是一種請求/響應(yīng)式的協(xié)議猛拴,即一個客戶端與服務(wù)器建立連接后,向服務(wù)器發(fā)送一個請求霞捡;服務(wù)器接到請求后坐漏,給予相應(yīng)的響應(yīng)信息。
首先我們要知道HTTP協(xié)議的工作是建立在TCP協(xié)議之上的,所以理解報文結(jié)構(gòu)對我們學(xué)習(xí)http協(xié)議非常重要赊琳。
HTTP請求報文
http請求報文由請求行街夭,請求頭部,空行以及請求包體組成躏筏,如下圖所示:
1.請求頭
由請求方法板丽,URL以及協(xié)議版本三部分組成,每個部分以空格分隔趁尼。常用的請求方法有:GET埃碱、POST、HEAD酥泞、PUT砚殿、DELETE、OPTIONS芝囤、TRACE瓮具、CONNECT;
- GET:當(dāng)客戶端要從服務(wù)器中讀取某個資源時凡人,使用GET 方法名党。GET 方法要求服務(wù)器將URL 定位的資源放在響應(yīng)報文的數(shù)據(jù)部分,回送給客戶端挠轴,即向服務(wù)器請求某個資源传睹。使用GET 方法時,請求參數(shù)和對應(yīng)的值附加在 URL 后面岸晦,利用一個問號(“?”)代表URL 的結(jié)尾與請求參數(shù)的開始欧啤,傳遞參數(shù)長度受限制。例如启上,/index.jsp?id=100&op=bind邢隧。
- POST:當(dāng)客戶端給服務(wù)器提供信息較多時可以使用POST 方法,POST 方法向服務(wù)器提交數(shù)據(jù)冈在,比如完成表單數(shù)據(jù)的提交倒慧,將數(shù)據(jù)提交給服務(wù)器處理。GET 一般用于獲取/查詢資源信息包券,POST 會附帶用戶數(shù)據(jù)纫谅,一般用于更新資源信息。POST 方法將請求參數(shù)封裝在HTTP 請求數(shù)據(jù)中溅固,以名稱/值的形式出現(xiàn)付秕,可以傳輸大量數(shù)據(jù);
2.請求頭部
請求頭部由關(guān)鍵字/值對組成侍郭,每行一對询吴,關(guān)鍵字和值用英文冒號“:”分隔掠河。請求頭部通知服務(wù)器有關(guān)于客戶端請求的信息,典型的請求頭有:
- User-Agent:產(chǎn)生請求的瀏覽器類型猛计;
- Accept:客戶端可識別的響應(yīng)內(nèi)容類型列表唠摹;星號 “ ” 用于按范圍將類型分組,用 “ / ” 指示+ 可接受全部類型有滑,用“ type/ ”指示可接受 type 類型的所有子類型;
- Accept-Language:客戶端可接受的自然語言嵌削;
- Accept-Encoding:客戶端可接受的編碼壓縮格式毛好;
- Accept-Charset:可接受的應(yīng)答的字符集;
- Host:請求的主機(jī)名苛秕,允許多個域名同處一個IP 地址肌访,即虛擬主機(jī);
- connection:連接方式(close 或 keepalive)艇劫;
- Cookie:存儲于客戶端擴(kuò)展字段吼驶,向同一域名的服務(wù)端發(fā)送屬于該域的cookie;
3.空行
最后一個請求頭之后是一個空行店煞,發(fā)送回車符和換行符蟹演,通知服務(wù)器以下不再有請求頭;
4.請求包體
請求包體不在 GET 方法中使用顷蟀,而是在POST 方法中使用酒请。POST 方法適用于需要客戶填寫表單的場合。與請求包體相關(guān)的最常使用的是包體類型 Content-Type 和包體長度 Content-Length鸣个;
HTTP響應(yīng)報文
HTTP 響應(yīng)報文由狀態(tài)行羞反、響應(yīng)頭部、空行 和 響應(yīng)包體 4 個部分組成囤萤,如下圖所示:
1.狀態(tài)行
狀態(tài)行由 HTTP 協(xié)議版本字段昼窗、狀態(tài)碼和狀態(tài)碼的描述文本 3 個部分組成,他們之間使用空格隔開涛舍;
狀態(tài)碼由三位數(shù)字組成澄惊,第一位數(shù)字表示響應(yīng)的類型,常用的狀態(tài)碼有五大類如下所示:
- 1xx:表示服務(wù)器已接收了客戶端請求富雅,客戶端可繼續(xù)發(fā)送請求缤削;
- 2xx:表示服務(wù)器已成功接收到請求并進(jìn)行處理;
- 3xx:表示服務(wù)器要求客戶端重定向吹榴;
- 4xx:表示客戶端的請求有非法內(nèi)容亭敢;
- 5xx:表示服務(wù)器未能正常處理客戶端的請求而出現(xiàn)意外錯誤;
狀態(tài)碼描述文本有如下取值: - 200 OK:表示客戶端請求成功图筹;
- 400 Bad Request:表示客戶端請求有語法錯誤帅刀,不能被服務(wù)器所理解让腹;
- 401 Unauthonzed:表示請求未經(jīng)授權(quán),該狀態(tài)代碼必須與 WWW-Authenticate 報頭域一起使用扣溺;
- 403 Forbidden:表示服務(wù)器收到請求骇窍,但是拒絕提供服務(wù),通常會在響應(yīng)正文中給出不提供服務(wù)的原因锥余;
- 404 Not Found:請求的資源不存在腹纳,例如,輸入了錯誤的URL驱犹;
- 500 Internal Server Error:表示服務(wù)器發(fā)生不可預(yù)期的錯誤嘲恍,導(dǎo)致無法完成客戶端的請求;
- 503 Service Unavailable:表示服務(wù)器當(dāng)前不能夠處理客戶端的請求雄驹,在一段時間之后佃牛,服務(wù)器可能會恢復(fù)正常;
2.響應(yīng)頭部
響應(yīng)頭可能包括:
- Location:Location響應(yīng)報頭域用于重定向接受者到一個新的位置医舆。例如:客戶端所請求的頁面已不存在原先的位置俘侠,為了讓客戶端重定向到這個頁面新的位置,服務(wù)器端可以發(fā)回Location響應(yīng)報頭后使用重定向語句蔬将,讓客戶端去訪問新的域名所對應(yīng)的服務(wù)器上的資源爷速;
Server:Server 響應(yīng)報頭域包含了服務(wù)器用來處理請求的軟件信息及其版本。它和 User-Agent 請求報頭域是相對應(yīng)的霞怀,前者發(fā)送服務(wù)器端軟件的信息遍希,后者發(fā)送客戶端軟件(瀏覽器)和操作系統(tǒng)的信息。 - Vary:指示不可緩存的請求頭列表里烦;
- Connection:連接方式凿蒜; 對于請求來說:close(告訴 WEB 服務(wù)器或者代理服務(wù)器,在完成本次請求的響應(yīng)后胁黑,斷開連接废封,不等待本次連接的后續(xù)請求了)。 keepalive(告訴WEB服務(wù)器或者代理服務(wù)器丧蘸,在完成本次請求的響應(yīng)后漂洋,保持連接,等待本次連接的后續(xù)請求)力喷; 對于響應(yīng)來說:close(連接已經(jīng)關(guān)閉)刽漂; keepalive(連接保持著,在等待本次連接的后續(xù)請求)弟孟; Keep-Alive:如果瀏覽器請求保持連接贝咙,則該頭部表明希望WEB 服務(wù)器保持連接多長時間(秒);例如:Keep-Alive:300拂募;
- WWW-Authenticate:WWW-Authenticate響應(yīng)報頭域必須被包含在401 (未授權(quán)的)響應(yīng)消息中庭猩,這個報頭域和前面講到的Authorization 請求報頭域是相關(guān)的窟她,當(dāng)客戶端收到 401 響應(yīng)消息,就要決定是否請求服務(wù)器對其進(jìn)行驗證蔼水。如果要求服務(wù)器對其進(jìn)行驗證震糖,就可以發(fā)送一個包含了Authorization 報頭域的請求;
3.空行
最后一個響應(yīng)頭部之后是一個空行趴腋,發(fā)送回車符和換行符吊说,通知服務(wù)器以下不再有響應(yīng)頭部。
4.響應(yīng)包體
服務(wù)器返回給客戶端的文本信息优炬;
HTTP 工作原理
HTTP 協(xié)議采用請求/響應(yīng)模型颁井。客戶端向服務(wù)器發(fā)送一個請求報文穿剖,服務(wù)器以一個狀態(tài)作為響應(yīng)蚤蔓。
以下是 HTTP 請求/響應(yīng)的步驟:
客戶端連接到web服務(wù)器:HTTP 客戶端與web服務(wù)器建立一個 TCP 連接卦溢;
客戶端向服務(wù)器發(fā)起 HTTP 請求:通過已建立的TCP 連接糊余,客戶端向服務(wù)器發(fā)送一個請求報文;
服務(wù)器接收 HTTP 請求并返回 HTTP 響應(yīng):服務(wù)器解析請求单寂,定位請求資源贬芥,服務(wù)器將資+ + 源副本寫到 TCP 連接,由客戶端讀刃觥蘸劈;
釋放 TCP 連接:若connection 模式為close,則服務(wù)器主動關(guān)閉TCP 連接尊沸,客戶端被動關(guān)閉連接威沫,釋放TCP 連接;若connection 模式為keepalive洼专,則該連接會保持一段時間棒掠,在該時間內(nèi)可以繼續(xù)接收請求;
-
客戶端瀏覽器解析HTML內(nèi)容:客戶端將服務(wù)器響應(yīng)的 html 文本解析并顯示屁商;
例如:在瀏覽器地址欄鍵入URL烟很,按下回車之后會經(jīng)歷以下流程:
1.瀏覽器向 DNS 服務(wù)器請求解析該 URL 中的域名所對應(yīng)的 IP 地址;
2.解析出 IP 地址后蜡镶,根據(jù)該 IP 地址和默認(rèn)端口 80雾袱,和服務(wù)器建立 TCP 連接;
3.瀏覽器發(fā)出讀取文件(URL 中域名后面部分對應(yīng)的文件)的HTTP 請求官还,該請求報文作為 TCP 三次握手的第三個報文的數(shù)據(jù)發(fā)送給服務(wù)器芹橡;
4.服務(wù)器對瀏覽器請求作出響應(yīng),并把對應(yīng)的 html 文本發(fā)送給瀏覽器望伦;
5.釋放 TCP 連接僻族;
6.瀏覽器將該 html 文本并顯示內(nèi)容;
HTTP 無狀態(tài)性
HTTP 協(xié)議是無狀態(tài)的(stateless)粘驰。也就是說,同一個客戶端第二次訪問同一個服務(wù)器上的頁面時述么,服務(wù)器無法知道這個客戶端曾經(jīng)訪問過蝌数,服務(wù)器也無法分辨不同的客戶端。HTTP 的無狀態(tài)特性簡化了服務(wù)器的設(shè)計度秘,使服務(wù)器更容易支持大量并發(fā)的HTTP 請求顶伞。
HTTP 持久連接
我們知道 HTTP 協(xié)議采用“請求-應(yīng)答”模式,當(dāng)使用普通模式剑梳,即非 Keep-Alive 模式時唆貌,每個請求/應(yīng)答客戶和服務(wù)器都要新建一個連接,完成之后立即斷開連接(HTTP協(xié)議為無連接的協(xié)議)垢乙;當(dāng)使用 Keep-Alive 模式(又稱持久連接锨咙、連接重用)時,Keep-Alive 功能使客戶端到服務(wù)器端的連接持續(xù)有效追逮,當(dāng)出現(xiàn)對服務(wù)器的后繼請求時酪刀,Keep-Alive 功能避免了建立或者重新建立連接。
在 HTTP 1.0 版本中钮孵,并沒有官方的標(biāo)準(zhǔn)來規(guī)定 Keep-Alive 如何工作骂倘,因此實際上它是被附加到 HTTP 1.0協(xié)議上,如果客戶端瀏覽器支持 Keep-Alive 巴席,那么就在HTTP請求頭中添加一個字段 Connection: Keep-Alive历涝,當(dāng)服務(wù)器收到附帶有 Connection: Keep-Alive 的請求時,它也會在響應(yīng)頭中添加一個同樣的字段來使用 Keep-Alive 漾唉。這樣一來荧库,客戶端和服務(wù)器之間的HTTP連接就會被保持,不會斷開(超過 Keep-Alive 規(guī)定的時間赵刑,意外斷電等情況除外)分衫,當(dāng)客戶端發(fā)送另外一個請求時,就使用這條已經(jīng)建立的連接料睛。
在 HTTP 1.1 版本中丐箩,默認(rèn)情況下所有連接都被保持,如果加入 "Connection: close" 才關(guān)閉恤煞。目前大部分瀏覽器都使用 HTTP 1.1 協(xié)議屎勘,也就是說默認(rèn)都會發(fā)起 Keep-Alive 的連接請求了,所以是否能完成一個完整的 Keep-Alive 連接就看服務(wù)器設(shè)置情況居扒。
由于 HTTP 1.0 沒有官方的 Keep-Alive 規(guī)范概漱,并且也已經(jīng)基本被淘汰,以下討論均是針對 HTTP 1.1 標(biāo)準(zhǔn)中的 Keep-Alive 展開的喜喂。
注意:
HTTP Keep-Alive 簡單說就是保持當(dāng)前的TCP連接瓤摧,避免了重新建立連接竿裂。
HTTP 長連接不可能一直保持,例如 Keep-Alive: timeout=5, max=100
照弥,表示這個TCP通道可以保持5秒腻异,max=100,表示這個長連接最多接收100次請求就斷開这揣。HTTP是一個無狀態(tài)協(xié)議悔常,這意味著每個請求都是獨立的,Keep-Alive沒能改變這個結(jié)果给赞。另外机打,Keep-Alive也不能保證客戶端和服務(wù)器之間的連接一定是活躍的,在HTTP1.1版本中也如此片迅。唯一能保證的就是當(dāng)連接被關(guān)閉時你能得到一個通知残邀,所以不應(yīng)該讓程序依賴于Keep-Alive的保持連接特性,否則會有意想不到的后果柑蛇。
使用長連接之后芥挣,客戶端、服務(wù)端怎么知道本次傳輸結(jié)束呢唯蝶?兩部分:1. 判斷傳輸數(shù)據(jù)是否達(dá)到了Content-Length 指示的大芯判恪遗嗽;2. 動態(tài)生成的文件沒有 Content-Length 粘我,它是分塊傳輸(chunked),這時候就要根據(jù) chunked 編碼來判斷痹换,chunked 編碼的數(shù)據(jù)在最后有一個空 chunked 塊征字,表明本次傳輸數(shù)據(jù)結(jié)束,詳見這里娇豫。什么是 chunked 分塊傳輸呢匙姜?下面我們就來介紹。
Transfer-Encoding: chunked(分塊編碼)
對于持久連接來講瀏覽器不能通過連接是否關(guān)閉來界定請求或響應(yīng)實體的邊界冯痢,當(dāng)我們發(fā)送完數(shù)據(jù)后氮昧,瀏覽器并不知道,所以無法第一時間關(guān)閉連接浦楣,當(dāng)再次訪問的時候就會出現(xiàn)等待的情況袖肥。
Transfer-Encoding 正是用來解決上面這個問題的,最新的 HTTP 規(guī)范里振劳,只定義了一種傳輸編碼:分塊編碼(chunked)椎组。
分塊編碼相當(dāng)簡單,在頭部加入 Transfer-Encoding: chunked 之后历恐,就代表這個報文采用了分塊編碼寸癌。這時专筷,報文中的實體需要改為用一系列分塊來傳輸。每個分塊包含十六進(jìn)制的長度值和數(shù)據(jù)蒸苇,長度值獨占一行磷蛹,長度不包括它結(jié)尾的 CRLF(\r\n),也不包括分塊數(shù)據(jù)結(jié)尾的 CRLF溪烤。最后一個分塊長度值必須為 0弦聂,對應(yīng)的分塊數(shù)據(jù)沒有內(nèi)容,表示實體結(jié)束氛什。
require('net').createServer(function(sock) {
sock.on('data', function(data) {
sock.write('HTTP/1.1 200 OK\r\n');
sock.write('Transfer-Encoding: chunked\r\n');
sock.write('\r\n');
sock.write('b\r\n');
sock.write('01234567890\r\n');
sock.write('5\r\n');
sock.write('12345\r\n');
sock.write('0\r\n');
sock.write('\r\n');
});
}).listen(9090, '127.0.0.1');
上面這個例子中莺葫,我在響應(yīng)頭中表明接下來的實體會采用分塊編碼,然后輸出了 11 字節(jié)的分塊枪眉,接著又輸出了 5 字節(jié)的分塊捺檬,最后用一個 0 長度的分塊表明數(shù)據(jù)已經(jīng)傳完了。用瀏覽器訪問這個服務(wù)贸铜,可以得到正確結(jié)果堡纬。可以看到蒿秦,通過這種簡單的分塊策略烤镐,很好的解決了前面提出的問題。
HTTP Pipelining(HTTP 管線化)
默認(rèn)情況下 HTTP 協(xié)議中每個傳輸層連接只能承載一個 HTTP 請求和響應(yīng)棍鳖,瀏覽器會在收到上一個請求的響應(yīng)之后炮叶,再發(fā)送下一個請求。在使用持久連接的情況下渡处,某個連接上消息的傳遞類似于請求1 -> 響應(yīng)1 -> 請求2 -> 響應(yīng)2 -> 請求3 -> 響應(yīng)3镜悉。
HTTP Pipelining(管線化)是將多個 HTTP 請求整批提交的技術(shù),在傳送過程中不需等待服務(wù)端的回應(yīng)医瘫。使用 HTTP Pipelining 技術(shù)之后侣肄,某個連接上的消息變成了類似這樣請求1 -> 請求2 -> 請求3 -> 響應(yīng)1 -> 響應(yīng)2 -> 響應(yīng)3。
注意下面幾點:
- 管線化機(jī)制通過持久連接(persistent connection)完成醇份,僅 HTTP/1.1 支持此技術(shù)(HTTP/1.0不支持)
- 只有 GET 和 HEAD 請求可以進(jìn)行管線化稼锅,而 POST 則有所限制
- 初次創(chuàng)建連接時不應(yīng)啟動管線機(jī)制,因為對方(服務(wù)器)不一定支持 HTTP/1.1 版本的協(xié)議
- 管線化不會影響響應(yīng)到來的順序僚纷,如上面的例子所示矩距,響應(yīng)返回的順序并未改變
HTTP /1.1 要求服務(wù)器端支持管線化,但并不要求服務(wù)器端也對響應(yīng)進(jìn)行管線化處理畔濒,只是要求對于管線化的請求不失敗即可 - 由于上面提到的服務(wù)器端問題剩晴,開啟管線化很可能并不會帶來大幅度的性能提升,而且很多服務(wù)器端和代理程序?qū)芫€化的支持并不好,因此現(xiàn)代瀏覽器如 Chrome 和 Firefox 默認(rèn)并未開啟管線化支持
會話跟蹤
1.什么是會話赞弥?
客戶端打開與服務(wù)器的連接發(fā)出請求到服務(wù)器響應(yīng)客戶端請求的全過程稱之為會話毅整。
2.什么是會話跟蹤?
會話跟蹤指的是對同一個用戶對服務(wù)器的連續(xù)的請求和接受響應(yīng)的監(jiān)視绽左。
3.為什么需要會話跟蹤悼嫉?
瀏覽器與服務(wù)器之間的通信是通過HTTP協(xié)議進(jìn)行通信的,而HTTP協(xié)議是”無狀態(tài)”的協(xié)議拼窥,它不能保存客戶的信息戏蔑,即一次響應(yīng)完成之后連接就斷開了,下一次的請求需要重新連接鲁纠,這樣就需要判斷是否是同一個用戶总棵,所以才有會話跟蹤技術(shù)來實現(xiàn)這種要求。
4.會話跟蹤常用的方法:
1>URL重寫
URL(統(tǒng)一資源定位符)是Web上特定頁面的地址改含,URL重寫的技術(shù)就是在URL結(jié)尾添加一個附加數(shù)據(jù)以標(biāo)識該會話,把會話ID通過URL的信息傳遞過去情龄,以便在服務(wù)器端進(jìn)行識別不同的用戶。
2>隱藏表單域
將會話ID添加到HTML表單元素中提交到服務(wù)器捍壤,此表單元素并不在客戶端顯示
3>Cookie
Cookie是Web服務(wù)器發(fā)送給客戶端的一小段信息骤视,客戶端請求時可以讀取該信息發(fā)送到服務(wù)器端,進(jìn)而進(jìn)行用戶的識別鹃觉。對于客戶端的每次請求专酗,服務(wù)器都會將Cookie發(fā)送到客戶端,在客戶端可以進(jìn)行保存,以便下次使用。
客戶端可以采用兩種方式來保存這個Cookie對象盗扇,一種方式是保存在客戶端內(nèi)存中祷肯,稱為臨時Cookie,瀏覽器關(guān)閉后這個Cookie對象將消失粱玲。另外一種方式是保存在客戶機(jī)的磁盤上躬柬,稱為永久Cookie拜轨。以后客戶端只要訪問該網(wǎng)站抽减,就會將這個Cookie再次發(fā)送到服務(wù)器上,前提是這個Cookie在有效期內(nèi)橄碾,這樣就實現(xiàn)了對客戶的跟蹤卵沉。
Cookie是可以被禁止的。
4>Session:
每一個用戶都有一個不同的session法牲,各個用戶之間是不能共享的史汗,是每個用戶所獨享的,在session中可以存放信息拒垃。
在服務(wù)器端會創(chuàng)建一個session對象停撞,產(chǎn)生一個sessionID來標(biāo)識這個session對象,然后將這個sessionID放入到Cookie中發(fā)送到客戶端,下一次訪問時戈毒,sessionID會發(fā)送到服務(wù)器艰猬,在服務(wù)器端進(jìn)行識別不同的用戶。
Session的實現(xiàn)依賴于Cookie埋市,如果Cookie被禁用冠桃,那么session也將失效。
相關(guān)文章
HTTP協(xié)議·筆試面試知識整理
http協(xié)議
HTTP 協(xié)議中的 Transfer-Encoding