本文為原創(chuàng)作品甲葬。歡迎轉(zhuǎn)載撇寞,轉(zhuǎn)載請注明出處:林東洲的博客 | Lindz Blog敢订。
作為一個(gè) Weber资厉,掌握必要的 HTTP/TCP 等協(xié)議十分必要厅缺。自從上學(xué)期學(xué)習(xí)了計(jì)算機(jī)網(wǎng)絡(luò),對于網(wǎng)絡(luò)這一塊有了一定的了解,再加上網(wǎng)上學(xué)習(xí)到的知識湘捎,通過本文記錄自己對 HTTP 協(xié)議的理解诀豁。
首先讓我們從一個(gè)問題入手,當(dāng)我們在瀏覽器中輸入 http://www.baidu.com 訪問百度的時(shí)候?yàn)g覽器做了哪些事情窥妇。(這里以 Chrome 瀏覽器為例)
- 首先 Chrome 搜索自身的 DNS 緩存舷胜。
如果 DNS 緩存中找到百度的IP地址,就跳過了接下來查找IP地址步驟活翩,直接訪問該 IP 地址烹骨。
- 搜索操作系統(tǒng)自身的 DNS 緩存。(瀏覽器沒有找到緩存或者緩存已經(jīng)失效)
- 讀取本身的 HOST 文件材泄。(如果前1.2步驟都沒有找到)
- 瀏覽器向?qū)拵н\(yùn)營商服務(wù)器或者域名服務(wù)器發(fā)起一個(gè) DNS 解析請求沮焕。
服務(wù)器先查看本身的緩存,若找到則返回百度首頁的 IP 地址拉宗,若沒有找到峦树,則服務(wù)器就發(fā)起一個(gè)迭代 DNS 解析的請求,如下圖:
從圖中我們可以看出域名服務(wù)器先向根域名服務(wù)器發(fā)起DNS解析請求旦事,接著迭代發(fā)起請求魁巩,直到找到所搜索的百度首頁的 IP 地址,并將它返回給瀏覽器姐浮。
- 拿到 IP 地址后谷遂,瀏覽器就向該 IP 所在的服務(wù)器發(fā)起 TCP 連接(即三次握手)。
- 連接建立起來之后卖鲤,瀏覽器就可以向服務(wù)器發(fā)起 HTTP 請求了肾扰。
這里比如訪問百度首頁,就向服務(wù)器發(fā)起 HTTP 中的 GET 請求扫尖。
- 服務(wù)器接受到這個(gè)請求后白对,根據(jù)路徑參數(shù),經(jīng)過后臺一些處理之后换怖,把處理后的結(jié)果返回給瀏覽器甩恼,如果是百度首頁,就可以把完整的 HTML 頁面代碼返回給瀏覽器沉颂。
- 瀏覽器拿到了百度首頁的完整 HTML 頁面代碼条摸,內(nèi)核和 JS 引擎就會解析和渲染這個(gè)頁面,里面的 JS铸屉,CSS钉蒲,圖片等靜態(tài)資源也通過一個(gè)個(gè) HTTP 請求進(jìn)行加載。
- 瀏覽器根據(jù)拿到的資源對頁面進(jìn)行渲染彻坛,最終把完整的頁面呈現(xiàn)給用戶顷啼。
- 如果瀏覽器沒有后續(xù)的請求踏枣,那么就會跟服務(wù)器端發(fā)起 TCP 斷開(即四次揮手)。
至此钙蒙,整個(gè)訪問過程就結(jié)束了茵瀑,這里只是簡單的概括,實(shí)際情況遠(yuǎn)比這些復(fù)雜躬厌。
HTTP 概念
超文本傳輸協(xié)議(HTTP马昨,HyperText Transfer Protocol)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。所有的WWW文件都必須遵守這個(gè)標(biāo)準(zhǔn)扛施。
舉個(gè)可能不太恰當(dāng)?shù)睦雍枧酰琓CP 就好比端與端之間溝通的橋梁,而 HTTP 就像是一個(gè)搬運(yùn)工疙渣,通過這個(gè)橋梁搬運(yùn)資源匙奴。
HTTP 特性:
- HTTP 構(gòu)建于 TCP/IP 協(xié)議之上,默認(rèn)端口號是 80
- HTTP 是無連接無狀態(tài)的
HTTP 分為兩個(gè)部分:請求和響應(yīng)昌阿。
HTTP 內(nèi)容
HTTP 請求:
HTTP 定義了在與服務(wù)器交互的不同方式饥脑,最常用的方法有 4 種,分別是 GET懦冰,POST,PUT谣沸, DELETE刷钢。URL 全稱為資源描述符,可以這么認(rèn)為:一個(gè) URL 地址乳附,對應(yīng)著一個(gè)網(wǎng)絡(luò)上的資源内地,而 HTTP 中的 GET,POST赋除,PUT阱缓,DELETE 就對應(yīng)著對這個(gè)資源的查詢,修改举农,增添荆针,刪除4個(gè)操作。
HTTP 請求由 3 個(gè)部分構(gòu)成颁糟,分別是:狀態(tài)行航背,請求頭(Request Header),請求正文棱貌。
狀態(tài)行由協(xié)議玖媚,狀態(tài)描述等構(gòu)成,各元素之間以空格分隔婚脱。
請求頭提供一些參數(shù)比如:Cookie今魔,用戶代理信息勺像,主機(jī)名等等。
請求正文就放一些發(fā)送的數(shù)據(jù)错森,一般 GET 請求會將參數(shù)放在 URL 中吟宦,也就是在請求頭中而請求正文一般為空,而 POST 請求將參數(shù)放在請求正文中问词。
- GET 一般用于信息獲取督函,比如剛才我們?yōu)g覽百度首頁,其使用的就是GET方法激挪。
GET請求報(bào)文實(shí)例:
GET 請求一般不會產(chǎn)生副作用辰狡,它僅僅只是獲取資源信息,就像數(shù)據(jù)庫查詢一樣垄分,不會修改宛篇、增加數(shù)據(jù),不會影響資源的狀態(tài)薄湿,并且對同一個(gè) URL 的多次GET請求應(yīng)該返回相同的結(jié)果叫倍。
- POST 請求表示可能會修改服務(wù)器上的資源。
注:
- GET 和 POST 請求參數(shù)位置不同,從上面兩個(gè)請求報(bào)文可以看出侠鳄,GET 請求對應(yīng)的參數(shù)放在 URL 中灵临,而 POST 請求對應(yīng)的參數(shù)放在 HTTP 請求主體中。
- GET 可提交的數(shù)據(jù)量受 URL 長度的限制蚕泽,而 POST 的數(shù)據(jù)量則沒有限制。
- 處于安全考慮桥嗤,在一些涉及安全的請求比如:登錄請求需要用 POST 提交表單须妻,而GET 請求一般用來獲取靜態(tài)資源。
HTTP 響應(yīng):
HTTP 響應(yīng)是服務(wù)器在客戶端發(fā)送 HTTP 請求后經(jīng)過一些處理而做出的響應(yīng)泛领,HTTP 響應(yīng)和 HTTP 請求相似荒吏,也是由三個(gè)部分構(gòu)成。分別是:狀態(tài)行渊鞋,響應(yīng)頭(Response Header)绰更,響應(yīng)正文。
HTTP 響應(yīng)中包含一個(gè)狀態(tài)碼篓像,用來表示服務(wù)器對客戶端響應(yīng)的結(jié)果动知。
狀態(tài)碼一般由3位構(gòu)成:
- 1xx : 表示請求已經(jīng)接受了,繼續(xù)處理员辩。
- 2xx : 表示請求已經(jīng)處理掉了盒粮。
- 3xx : 重定向。
- 4xx : 一般表示客戶端有錯(cuò)誤奠滑,請求無法實(shí)現(xiàn)丹皱。
- 5xx : 一般為服務(wù)器端的錯(cuò)誤妒穴。
比較常用的狀態(tài)碼:
- 200 OK 客戶端請求成功。
- 301 Moved Permanently 請求永久重定向摊崭。
- 302 Moved Temporarily 請求臨時(shí)重定向讼油。
- 304 Not Modified 文件未修改,可以直接使用緩存的文件呢簸。
- 400 Bad Request 由于客戶端請求有語法錯(cuò)誤矮台,不能被服務(wù)器所理解。
- 401 Unauthonzed 請求未經(jīng)授權(quán)根时,無法訪問瘦赫。
- 403 Forbidden 服務(wù)器收到請求,但是拒絕提供服務(wù)蛤迎。服務(wù)器通常會在響應(yīng)正文中給出不提供服務(wù)的原因确虱。
- 404 Not Found 請求的資源不存在,比如輸入了錯(cuò)誤的URL替裆。
- 500 Internal Server Error 服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤校辩,導(dǎo)致無法完成客戶端的請求。
- 503 Service Unavailable 服務(wù)器當(dāng)前不能夠處理客戶端的請求辆童,在一段時(shí)間之后宜咒,服務(wù)器可能會恢復(fù)正常。
下面是一個(gè) HTTP 響應(yīng)的例子:
知道了 HTTP 請求和響應(yīng)后把鉴,一個(gè)完整的流程一般是這樣的:
通常荧呐,由 HTTP 客戶端發(fā)起一個(gè)請求,建立一個(gè)到服務(wù)器指定端口(默認(rèn)是 80 端口)的 TCP 連接纸镊。HTTP 服務(wù)器則在那個(gè)端口監(jiān)聽客戶端發(fā)送過來的請求。一旦收到請求概疆,服務(wù)器(向客戶端)發(fā)回一個(gè)狀態(tài)行逗威,比如"HTTP/1.1 200 OK",和(響應(yīng)的)消息岔冀,消息的消息體可能是請求的文件凯旭、錯(cuò)誤消息、或者其它一些信息使套。
HTTP 使用 TCP 而不是 UDP 的原因在于(打開)一個(gè)網(wǎng)頁必須傳送很多數(shù)據(jù)罐呼,而 TCP 協(xié)議提供傳輸控制,按順序組織數(shù)據(jù)侦高,和錯(cuò)誤糾正嫉柴。
其它
了解以上那些概念后,已經(jīng)對 HTTP 協(xié)議有了大致的了解了奉呛。下面介紹一些 HTTP 實(shí)現(xiàn)中具體內(nèi)容计螺。
會話追蹤
- 會話:客戶端向服務(wù)器端發(fā)起請求到服務(wù)端響應(yīng)客戶端請求的全過程夯尽。
- 會話跟蹤:會話追蹤指的是服務(wù)器對用戶響應(yīng)的監(jiān)視。
為什么需要會話跟蹤:
瀏覽器與服務(wù)器之間的通信是通過 HTTP 協(xié)議進(jìn)行通信的登馒,而 HTTP 協(xié)議是”無狀態(tài)”的協(xié)議匙握,它不能保存客戶的信息,即一次響應(yīng)完成之后連接就斷開了陈轿,下一次的請求需要重新連接圈纺,這樣就需要判斷是否是同一個(gè)用戶,所以才有會話跟蹤技術(shù)來實(shí)現(xiàn)這種要求麦射。
會話跟蹤常用方法:
- URL 重寫:URL 重寫技術(shù)就是在 URL 結(jié)尾添加一個(gè)附加數(shù)據(jù)以標(biāo)識該會話蛾娶,把會話 ID 通過 URL 的信息傳遞過去,以便在服務(wù)器進(jìn)行識別不同的用戶法褥。
- 隱藏表單域:將會話ID添加到HTML表單元素中提交到服務(wù)器茫叭,此表單元素并不在客戶端顯示。
- Cookie:Cookie 是 Web 服務(wù)器發(fā)送給客戶端的一小段信息半等,客戶端請求時(shí)可以讀取該信息發(fā)送給服務(wù)器端揍愁,進(jìn)而進(jìn)行用戶的識別,對于客戶端的每次請求杀饵,服務(wù)器都會將 Cookie 發(fā)送到客戶端莽囤,客戶端保存下來,以便下次使用切距。
客戶端可以采用兩種方式來保存這個(gè)Cookie對象朽缎,一種方式是保存在客戶端內(nèi)存中,稱為臨時(shí)Cookie谜悟,瀏覽器關(guān)閉后這個(gè)Cookie對象將消失话肖。
另外一種方式是保存在客戶機(jī)的磁盤上,稱為永久Cookie葡幸。以后客戶端只要訪問該網(wǎng)站最筒,就會將這個(gè)Cookie再次發(fā)送到服務(wù)器上,前提是這個(gè)Cookie在有效期內(nèi)蔚叨,這樣就實(shí)現(xiàn)了對客戶的跟蹤床蜘。
Cookie 是可以被禁止的,當(dāng)你打開 Chrome蔑水,在設(shè)置里面關(guān)閉 Cookie邢锯,那么你將再也無法登錄淘寶頁面。
- Session:在服務(wù)器端會創(chuàng)建一個(gè) session 對象搀别,產(chǎn)生一個(gè) sessionID 來標(biāo)識這個(gè) session 對象丹擎,然后將這個(gè) sessionID 放入到 Cookie 中發(fā)送到客戶端,下一次訪問時(shí)领曼,sessionID 會發(fā)送到服務(wù)器鸥鹉,在服務(wù)器端進(jìn)行識別不同的用戶蛮穿。
每一個(gè)用戶都有一個(gè)不同的 session,各個(gè)用戶之間是不能共享的毁渗,是每個(gè)用戶所獨(dú)享的践磅,在 session 中可以存放信息。
Session的實(shí)現(xiàn)依賴于Cookie灸异,如果Cookie被禁用府适,那么session也將失效。
條件 GET:
HTTP 條件 GET 是 HTTP 協(xié)議為了減少不必要的帶寬浪費(fèi)肺樟,提出的一種方案檐春。
- HTTP 條件 GET 使用時(shí)機(jī):客戶端之前已經(jīng)訪問過某網(wǎng)站,并打算再次訪問該站點(diǎn)么伯。
- HTTP 條件 GET 使用的方法:客戶端向服務(wù)器發(fā)送一個(gè)包詢問是否在上一次訪問網(wǎng)站的時(shí)間后是否更改了頁面疟暖,如果服務(wù)器沒有更新,顯然不需要把整個(gè)網(wǎng)頁傳給客戶端田柔,客戶端只要使用本地緩存即可俐巴,如果服務(wù)器對照客戶端給出的時(shí)間已經(jīng)更新了客戶端請求的網(wǎng)頁,則發(fā)送這個(gè)更新了的網(wǎng)頁給用戶硬爆。
下面是一個(gè)具體的發(fā)送接受報(bào)文示例:
客戶端發(fā)送請求:
第一次請求時(shí)欣舵,服務(wù)器端返回請求數(shù)據(jù),之后的請求缀磕,服務(wù)器根據(jù)請求中的 If-Modified-Since 字段判斷響應(yīng)文件沒有更新缘圈,如果沒有更新,服務(wù)器返回一個(gè) 304 Not Modified響應(yīng)袜蚕,告訴瀏覽器請求的資源在瀏覽器上沒有更新糟把,可以使用已緩存的上次獲取的文件。
如果服務(wù)器端資源已經(jīng)更新的話牲剃,就返回正常的響應(yīng)糊饱。
持久連接:
我們知道 HTTP 協(xié)議采用“請求-應(yīng)答”模式,當(dāng)使用普通模式颠黎,即非 Keep-Alive 模式時(shí),每個(gè)請求/應(yīng)答客戶和服務(wù)器都要新建一個(gè)連接滞项,完成之后立即斷開連接(HTTP協(xié)議為無連接的協(xié)議)狭归;當(dāng)使用 Keep-Alive 模式(又稱持久連接、連接重用)時(shí)文判,Keep-Alive 功能使客戶端到服務(wù)器端的連接持續(xù)有效过椎,當(dāng)出現(xiàn)對服務(wù)器的后繼請求時(shí),Keep-Alive 功能避免了建立或者重新建立連接戏仓。
在 HTTP 1.0 版本中疚宇,并沒有官方的標(biāo)準(zhǔn)來規(guī)定 Keep-Alive 如何工作亡鼠,因此實(shí)際上它是被附加到 HTTP 1.0協(xié)議上,如果客戶端瀏覽器支持 Keep-Alive 敷待,那么就在HTTP請求頭中添加一個(gè)字段 Connection: Keep-Alive间涵,當(dāng)服務(wù)器收到附帶有 Connection: Keep-Alive 的請求時(shí),它也會在響應(yīng)頭中添加一個(gè)同樣的字段來使用 Keep-Alive 榜揖。這樣一來勾哩,客戶端和服務(wù)器之間的HTTP連接就會被保持,不會斷開(超過 Keep-Alive 規(guī)定的時(shí)間举哟,意外斷電等情況除外)思劳,當(dāng)客戶端發(fā)送另外一個(gè)請求時(shí),就使用這條已經(jīng)建立的連接妨猩。
在 HTTP 1.1 版本中潜叛,默認(rèn)情況下所有連接都被保持,如果加入 "Connection: close" 才關(guān)閉壶硅。目前大部分瀏覽器都使用 HTTP 1.1 協(xié)議威兜,也就是說默認(rèn)都會發(fā)起 Keep-Alive 的連接請求了,所以是否能完成一個(gè)完整的 Keep-Alive 連接就看服務(wù)器設(shè)置情況森瘪。
由于 HTTP 1.0 沒有官方的 Keep-Alive 規(guī)范牡属,并且也已經(jīng)基本被淘汰,以下討論均是針對 HTTP 1.1 標(biāo)準(zhǔn)中的 Keep-Alive 展開的扼睬。
小結(jié):
- 會話跟蹤和條件 GET 是有不同的逮栅,它們分別運(yùn)用于不同的場景。會話跟蹤是服務(wù)器用來表示標(biāo)識區(qū)分不同的用戶窗宇,而有條件 GET 則用戶多次訪問相同的頁面措伐,用來減少帶寬的浪費(fèi),兩者有本質(zhì)上的區(qū)別军俊。
- HTTP Keep-Alive 簡單說就是保持當(dāng)前的TCP連接侥加,避免了重新建立連接。
- HTTP 是一個(gè)無狀態(tài)無連接的協(xié)議粪躬,那么這是不是與 Keep-Alive 沖突担败?
個(gè)人認(rèn)為:Keep-Alive 與無連接的特性沖突,而對于無狀態(tài)的特性兩者并無矛盾镰官,HTTP 無狀態(tài)無連接是在 1.0 版本中就規(guī)定的提前,而 Keep-Alive 則是在 1.1 版本中才被添加入規(guī)范。
無連接的意思是限制每個(gè)連接只有一個(gè)請求的意思泳唠,在服務(wù)器處理完客戶的請求狈网,并收到客戶的反應(yīng),即斷開。通過這種方式可以節(jié)省傳輸時(shí)間拓哺。
Keep-Alive 確實(shí)破壞了這一特性勇垛,而無狀態(tài)協(xié)議則意味著每個(gè)請求都是獨(dú)立的,互不干擾的士鸥,互相沒有記憶的闲孤。所以才需要有會話跟蹤這種機(jī)制來識別用戶。
參考鏈接: