【網絡通信 -- 直播】IM 學習系列 -- 網絡通信協議簡介(HTTP 協議 二)

【網絡通信 -- 直播】IM 學習系列 -- 網絡通信協議簡介(HTTP 協議 二)

【1】一次 HTTP 請求過程

圖示爲一次 HTTP 請求的抓包數據分析

圖示爲瀏覽器與 Web 服務器通過 HTTP 協議交換過程

大致交換流程如下

1. 瀏覽器從地址欄的輸入中獲得服務器的 IP 地址和端口號;
2. 瀏覽器用 TCP 的三次握手與服務器建立連接;
3. 瀏覽器向服務器發送拼好的報文;
4. 服務器收到報文後處理請求,同樣拼好報文再發給瀏覽器;
5. 瀏覽器解析報文,渲染輸出頁面;

圖示爲實際的互聯網場景

【2】HTTP 報文

HTTP 協議的核心部分是其傳輸的報文內容;
HTTP 協議需要在實際傳輸的數據前附加一些頭數據,HTTP 是一個“純文本”的協議,因此頭數據都是 ASCII 碼的文本;
HTTP 協議的請求報文和響應報文的結構基本相同,由三大部分組成:
起始行(start line),描述請求或響應的基本信息;
頭部字段集合(header),使用 key-value 形式更詳細地說明報文;
消息正文(entity),實際傳輸的數據,不一定是純文本,可以是圖片、視頻等二進制數據;
這其中前兩部分起始行和頭部字段經常又合稱爲“請求頭”或“響應頭”,消息正文又稱爲“實體”,但與“header”對應,很多時候就直接稱爲“body”,HTTP 協議規定報文必須有 header,但可以沒有 body,而且在 header 之後必須要有一個“空行”,也就是“CRLF”,十六進制的“0D0A”;

請求行由三部分構成:
請求方法,是一個動詞,如 GET/POST,表示對資源的操作;
請求目標:通常是一個 URI,標記了請求方法要操作的資源;
版本號:表示報文使用的 HTTP 協議版本;

圖示爲請求行構成

狀態行由三部分構成:
版本號,表示報文使用的 HTTP 協議版本;
狀態碼,一個三位數,用代碼的形式表示處理的結果,比如 200 是成功,500 是服務器錯誤;
原因,作爲數字狀態碼補充,更詳細的解釋文字;

圖示爲狀態行構成

圖示爲請求頭構成

圖示爲抓包獲取的請求頭數據

圖示爲響應頭構成

圖示爲抓包獲取的響應頭數據

頭字段需要注意點:
1. 字段名不區分大小寫,但首字母大寫的可讀性更好;
2. 字段名裏不允許出現空格,可以使用連字符“-”,但不能使用下劃線“_”;
3. 字段名後面必須緊接着“:”,不能有空格,而“:”後的字段值前可以有多個空格;
4. 字段的順序是沒有意義的,可以任意排列不影響語義;
5. 字段原則上不能重複,除非這個字段本身的語義允許;

常用頭字段
HTTP 協議規定了非常多的頭部字段,實現各種各樣的功能,但基本上可以分爲四大類:
通用字段,在請求頭和響應頭裏都可以出現;
請求字段,僅能出現在請求頭裏,進一步說明請求信息或者額外的附加條件;
響應字段,僅能出現在響應頭裏,補充說明響應報文的信息;
實體字段,屬於通用字段,但專門描述 body 的額外信息;

Host 字段
Host 字段,屬於請求字段,只能出現在請求頭裏,同時也是唯一一個 HTTP/1.1 規範裏要求必須出現的字段,Host 字段告訴服務器這個請求應該由哪個主機來處理,當一臺計算機上託管了多個虛擬主機的時候,服務器端就需要用 Host 字段來選擇,有點像是一個簡單的“路由重定向”;

User-Agent 字段
User-Agent 是請求字段,只出現在請求頭裏,使用一個字符串來描述發起 HTTP 請求的客戶端,服務器可以依據該字段來返回最合適此瀏覽器顯示的頁面,有的比較“誠實”的爬蟲會在 User-Agent 裏用“spider”標明自己是爬蟲,可以利用這個字段實現簡單的反爬蟲策略;

Date 字段
Date 字段是一個通用字段,通常出現在響應頭裏,表示 HTTP 報文創建的時間,客戶端可以使用這個時間再搭配其他字段決定緩存策略;

Server 字段
Server 字段是響應字段,只能出現在響應頭裏,告訴客戶端當前正在提供 Web 服務的軟件名稱和版本號,Server 字段不是必須要出現的,因爲這會把服務器的一部分信息暴露給外界;

Content-Length 字段
Content-Length 字段是實體字段,表示報文裏 body 的長度,也就是請求頭或響應頭空行後面數據的長度,服務器看到這個字段,就知道了後續有多少數據,可以直接接收;如果沒有這個字段,那麼 body 就是不定長的,需要使用 chunked 方式分段傳輸;

標準請求方法

目前 HTTP/1.1 規定了八種方法,單詞都必須是大寫的形式
GET,獲取資源,可以理解爲讀取或者下載數據;
HEAD,獲取資源的元信息;
POST,向資源提交數據,相當於寫入或上傳數據;
PUT,類似 POST;
DELETE,刪除資源;
CONNECT,建立特殊的連接隧道;
OPTIONS,列出可對資源實行的方法;
TRACE,追蹤請求 - 響應的傳輸路徑;

GET/HEAD
GET 方法的含義是請求從服務器獲取資源,這個資源既可以是靜態的文本、頁面、圖片、視頻,也可以是由 PHP、Java 動態生成的頁面或者其他格式的數據;GET 方法雖然基本動作比較簡單,但搭配 URI 和其他頭字段就能實現對資源更精細的操作;
例如,在 URI 後
使用“#”,就可以在獲取頁面後直接定位到某個標籤所在的位置;
使用 If-Modified-Since 字段就變成了“有條件的請求”,僅當資源被修改時纔會執行獲取動作;
使用 Range 字段就是“範圍請求”,只獲取資源的一部分數據;

HEAD 方法與 GET 方法類似,也是請求從服務器獲取資源,服務器的處理機制也是一樣的,但服務器不會返回請求的實體數據,只會傳回響應頭,也就是資源的“元信息”;HEAD 方法可以看做是 GET 方法的一個“簡化版”或者“輕量版”;因爲它的響應頭與 GET 完全相同,所以可以用在很多並不真正需要資源的場合,避免傳輸 body 數據的浪費;

POST/PUT
POST 和 PUT 方法向 URI 指定的資源提交數據,數據就放在報文的 body 裏;
POST 的作用是向服務器提交數據,POST 表示的是“新建”“create”的含義;
PUT 的作用是向服務器提交數據,PUT 表示“修改”“update”的含義;

DELETE 方法指示服務器刪除資源,因爲這個動作危險性太大,所以通常服務器不會執行真正的刪除操作,而是對資源做一個刪除標記;
CONNECT 方法指示服務器爲客戶端和另一臺遠程服務器建立一條特殊的連接隧道,這時 Web 服務器在中間充當了代理的角色;
OPTIONS 方法指示服務器列出可對資源實行的操作方法,在響應頭的 Allow 字段裏返回;
TRACE 方法多用於對 HTTP 鏈路的測試或診斷,可以顯示出請求 - 響應的傳輸路徑,存在漏洞,會泄漏網站的信息,通常禁止使用;

安全與冪等
HTTP 協議裏,所謂的“安全”是指請求方法不會“破壞”服務器上的資源,即不會對服務器上的資源造成實質的修改;
按照這個定義,只有 GET 和 HEAD 方法是“安全”的,因爲它們是“只讀”操作,只要服務器不故意曲解請求方法的處理方式,無論 GET 和 HEAD 操作多少次,服務器上的數據都是“安全的”;而 POST/PUT/DELETE 操作會修改服務器上的資源,增加或刪除數據,所以是“不安全”的;
HTTP 協議裏,所謂的“冪等”實際上是一個數學用語,被借用到了 HTTP 協議裏,意思是多次執行相同的操作,結果也都是相同的,即多次“冪”後結果“相等”;很顯然,GET 和 HEAD 既是安全的也是冪等的,DELETE 可以多次刪除同一個資源,效果都是“資源不存在”,所以也是冪等的;POST 和 PUT 的冪等性質按照 RFC 裏的語義,POST 是“新增或提交數據”,多次提交數據會創建多個資源,所以不是冪等的;PUT 是“替換或更新數據”,多次更新一個資源,資源還是會第一次更新的狀態,所以是冪等的;

【3】URI

URI 的格式
URI 本質上是一個唯一地標記資源的位置或者名字的字符串,它不僅能夠標記萬維網的資源,也可以標記郵件系統、本地文件系統等任意資源;而“資源”既可以是存在磁盤上的靜態文本、頁面數據,也可以是由 Java、PHP 提供的動態服務;
URI 最常用的形式由 scheme、host:port、path 和 query 四個部分組成;

scheme “方案名”或者“協議名”,表示資源的訪問協議
authority 表示資源所在的主機名
path 標記資源所在位置, path 部分必須以“/”開始
query 在 path 之後,用一個“?”開始,但不包含“?”,表示對資源附加的額外要求
query 查詢參數是多個“key=value”的字符串,這些 KV 值用字符“&”連接,瀏覽器和服務器都可以按照這個格式把長串的查詢參數解析成可理解的字典或關聯數組形式

查詢參數和頭字段兩者的形式很相近,query 是 key-value,頭字段也是 key-value,它們有什麼區別,
在發送請求時應該如何正確地使用它們
1. 查詢參數是與 URI 關聯在一起的,針對的就是資源(URI),是長期、穩定的;
頭字段是與一次 HTTP 請求關聯的,針對的是本次請求報文,是短期、臨時的;
即兩者的作用域和時效性是不同的;
2. 資源的屬性用查詢參數來描述,而如壓縮傳輸或者控制緩存的時間等操作並不是資源本身固有的特性用頭字段來描述

身份信息“user:passwd@”,表示登錄主機時的用戶名和密碼
片段標識符“#fragment” 是 URI 所定位的資源內部的一個“錨點”或者說是“標籤”,瀏覽器可以在獲取資源後直接跳轉到它指示的位置,片段標識符僅能由客戶端使用

URI 的編碼
URI 轉義的規則直接把非 ASCII 碼或特殊字符轉換成十六進制字節值,然後前面再加上一個“%”;
例如,空格被轉義成“%20”,“?”被轉義成“%3F”,中文、日文等則通常使用 UTF-8 編碼後再轉義,例如“銀河”會被轉義成“%E9%93%B6%E6%B2%B3”;

【4】狀態碼

狀態碼是三位數取值範圍就是從 000 到 999
狀態碼分類
1×× 提示信息,表示目前是協議處理的中間狀態,還需要後續的操作;
2×× 成功,報文已經收到並被正確處理;
3×× 重定向,資源位置發生變動,需要客戶端重新發送請求;
4×× 客戶端錯誤,請求報文有誤,服務器無法處理;
5×× 服務器錯誤,服務器在處理請求時內部發生了錯誤;

101 Switching Protocols 客戶端使用 Upgrade 頭字段,要求在 HTTP 協議的基礎上改成其他的協議繼續通信,如果服務器也同意變更協議,就會發送狀態碼 101;

200 OK 表示一切正常,如果是非 HEAD 請求,通常在響應頭後都會有 body 數據;
204 No Content 含義與 200 OK 基本相同,但響應頭後沒有 body 數據;
206 Partial Content 是 HTTP 分塊下載或斷點續傳的基礎,在客戶端發送“範圍請求”、要求獲取資源的部分數據時出現,與 200 一樣,也是服務器成功處理了請求,但 body 裏的數據不是資源的全部,而是其中的一部分

301 Moved Permanently “永久重定向”,含義是此次請求的資源已經不存在了,需要改用改用新的 URI 再次訪問;
302 Found “臨時重定向”,意思是請求的資源還在,但需要暫時用另一個 URI 來訪問;
304 Not Modified 用於 If-Modified-Since 等條件請求,表示資源未修改,用於緩存控制;它不具有通常的跳轉含義,但可以理解成“重定向已到緩存的文件”

400 Bad Request 表示請求報文有錯誤,只是一個籠統的錯誤;
403 Forbidden 表示服務器禁止訪問資源;服務器可以在 body 裏詳細說明拒絕請求的原因;
404 Not Found 表示資源在本服務器上未找到;
405 Method Not Allowed 表示不允許使用某些方法操作資源;
406 Not Acceptable 表示資源無法滿足客戶端請求的條件;
408 Request Timeout 表示請求超時,服務器等待了過長的時間;
409 Conflict 表示多個請求發生了衝突,可以理解爲多線程併發時的競態;
413 Request Entity Too Large 表示請求報文裏的 body 太大;
414 Request-URI Too Long 表示請求行裏的 URI 太大;
429 Too Many Requests 表示客戶端發送了太多的請求,通常是由於服務器的限連策略;
431 Request Header Fields Too Large 表示請求頭某個字段或總體太大;

500 Internal Server Error 通用的錯誤碼,服務器的錯誤客戶端無法知道;
501 Not Implemented 表示客戶端請求的功能還不支持;
502 Bad Gateway 通常是服務器作爲網關或者代理時返回的錯誤碼,表示服務器自身工作正常,訪問後端服務器時發生了錯誤,但具體的錯誤原因也是不知道的;
503 Service Unavailable 表示服務器當前很忙,暫時無法響應服務;

【5】HTTP 特點

靈活可擴展、可靠傳輸、應用層協議、請求 - 應答、無狀態(有連接無狀態,順序發包順序收包,按照收發的順序管理報文);

無狀態優勢
1. 服務器沒有“記憶能力”,便不需要額外的資源來記錄狀態信息,不僅實現上會簡單一些,而且還能減輕服務器的負擔,能夠把更多的 CPU 和內存用來對外提供服務;
2. 無狀態也表示服務器都是相同的,沒有“狀態”的差異,所以可以很容易地組成集羣,讓負載均衡把請求轉發到任意一臺服務器,不會因爲狀態不一致導致處理出錯,使用“堆機器”的“笨辦法”輕鬆實現高併發高可用;
無狀態的劣勢

服務器沒有“記憶能力”,便無法支持需要連續多個步驟的“事務”操作;
例如電商購物,首先要登錄,然後添加購物車,再下單、結算、支付,這一系列操作都需要知道用戶的身份纔行,但“無狀態”服務器是不知道這些請求是相互關聯的,每次都得問一遍身份信息,不僅麻煩,而且還增加了不必要的數據傳輸量;

HTTP 性能

請求 - 應答模式加劇了 HTTP 的性能問題,比如著名的“隊頭阻塞”(Head-of-line blocking),當順序發送的請求序列中的一個請求因爲某種原因被阻塞時,在後面排隊的所有請求也一併被阻塞,會導致客戶端遲遲收不到數據;

參考致謝
本博客爲博主的學習實踐總結,並參考了衆多博主的博文,在此表示感謝,博主若有不足之處,請批評指正。

【1】透視HTTP協議

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章