音視頻流媒體開(kāi)發(fā)-目錄
iOS知識(shí)點(diǎn)-目錄
Android-目錄
Flutter-目錄
數(shù)據(jù)結(jié)構(gòu)與算法-目錄
uni-pp-目錄
進(jìn)?步參考?檔:
《HTTP協(xié)議》
《圖解HTTP》
報(bào)?結(jié)構(gòu)
你也許對(duì) TCP/UDP 的報(bào)?格式有所了解,拿 TCP 報(bào)?來(lái)舉例摩泪,它在實(shí)際要傳輸?shù)臄?shù)據(jù)之前附加了?個(gè)20 字節(jié)的頭部數(shù)據(jù)濒生,存儲(chǔ) TCP 協(xié)議必須的額外信息火架,例如發(fā)送?的端?號(hào)闻牡、接收?的端?號(hào)、包序號(hào)、標(biāo)志位等等。
有了這個(gè)附加的 TCP 頭袭厂,數(shù)據(jù)包才能夠正確傳輸,到了?的地后把頭部去掉饭入,就可以拿到真正的數(shù)據(jù)嵌器。
HTTP 協(xié)議也是與 TCP/UDP 類似肛真,同樣也需要在實(shí)際傳輸?shù)臄?shù)據(jù)前附加?些頭數(shù)據(jù)谐丢,不過(guò)與 TCP/UDP不同的是,它是?個(gè)“純?本”的協(xié)議蚓让,所以頭數(shù)據(jù)都是 ASCII 碼的?本乾忱,可以很容易地??眼閱讀,不?借助程序解析也能夠看懂历极。
HTTP 協(xié)議的請(qǐng)求報(bào)?和響應(yīng)報(bào)?的結(jié)構(gòu)基本相同窄瘟,由三?部分組成:
- 起始?(start line):描述請(qǐng)求或響應(yīng)的基本信息;
- 頭部字段集合(header):使? key-value 形式更詳細(xì)地說(shuō)明報(bào)?趟卸;
- 消息正?(entity):實(shí)際傳輸?shù)臄?shù)據(jù)蹄葱,它不?定是純?本氏义,可以是圖?、視頻等?進(jìn)制數(shù)據(jù)图云。
這其中前兩部分起始?和頭部字段經(jīng)常?合稱為“請(qǐng)求頭”或“響應(yīng)頭”惯悠,消息正??稱為“實(shí)體”,但與“header”對(duì)應(yīng)竣况,很多時(shí)候就直接稱為“body”克婶。
HTTP 協(xié)議規(guī)定報(bào)?必須有 header,但可以沒(méi)有 body丹泉,?且在 header 之后必須要有?個(gè)“空?”情萤,也就是“CRLF”,?六進(jìn)制的“0D0A”摹恨。
所以筋岛,?個(gè)完整的 HTTP 報(bào)?就像是下圖的這個(gè)樣?,注意在 header 和 body 之間有?個(gè)“空?”晒哄。
看?下我們之前? Wireshark 抓的包泉蝌。
在這個(gè)瀏覽器發(fā)出的請(qǐng)求報(bào)??。
第??“GET / HTTP/1.1”就是請(qǐng)求?揩晴,?后?的“Host”“Connection”等等都屬于 header勋陪,報(bào)?的最后是?個(gè)空??結(jié)束,沒(méi)有 body硫兰。
在很多時(shí)候诅愚,特別是瀏覽器發(fā)送 GET 請(qǐng)求的時(shí)候都是這樣,HTTP 報(bào)?經(jīng)常是只有 header ?沒(méi) body劫映。
請(qǐng)求?
了解了 HTTP 報(bào)?的基本結(jié)構(gòu)后违孝,我們來(lái)看看請(qǐng)求報(bào)??的起始?也就是請(qǐng)求?(request line),它簡(jiǎn)要地描述了客戶端想要如何操作服務(wù)器端的資源泳赋。
請(qǐng)求?由三部分構(gòu)成:
1. 請(qǐng)求?法:是?個(gè)動(dòng)詞雌桑,如 GET/POST,表示對(duì)資源的操作祖今;
2. 請(qǐng)求?標(biāo):通常是?個(gè) URI校坑,標(biāo)記了請(qǐng)求?法要操作的資源;
3. 版本號(hào):表示報(bào)?使?的 HTTP 協(xié)議版本千诬。
這三個(gè)部分通常使?空格(space)來(lái)分隔耍目,最后要? CRLF 換?表示結(jié)束。(圖示SP代表空格)
還是? Wireshark 抓包的數(shù)據(jù)來(lái)舉例:
GET / HTTP/1.1\r\n
GET /live/livestream.m3u8 HTTP/1.1\r\n
在這個(gè)請(qǐng)求??徐绑,“GET”是請(qǐng)求?法邪驮,“/”是請(qǐng)求?標(biāo),“HTTP/1.1”是版本號(hào)傲茄,把這三部分連起來(lái)毅访,意思就是“服務(wù)器你好沮榜,我想獲取?站根?錄下的默認(rèn)?件,我?的協(xié)議版本號(hào)是 1.1喻粹,請(qǐng)不要? 1.0 或者 2.0回復(fù)我敞映。”
別看請(qǐng)求?就??磷斧,貌似很簡(jiǎn)單振愿,其實(shí)這??的“講究”是?常多的,尤其是前?的請(qǐng)求?法和請(qǐng)求?標(biāo)弛饭,組合起來(lái)變化多端冕末,后?我還會(huì)詳細(xì)介紹。
狀態(tài)?
看完了請(qǐng)求?侣颂,我們?cè)倏错憫?yīng)報(bào)??的起始?档桃,在這?它不叫“響應(yīng)?”,?是叫“狀態(tài)?”(status line)憔晒,意思是服務(wù)器響應(yīng)的狀態(tài)藻肄。
?起請(qǐng)求?來(lái)說(shuō),狀態(tài)?要簡(jiǎn)單?些拒担,同樣也是由三部分構(gòu)成:
- 版本號(hào):表示報(bào)?使?的 HTTP 協(xié)議版本嘹屯;
- 狀態(tài)碼:?個(gè)三位數(shù),?代碼的形式表示處理的結(jié)果从撼,?如 200 是成功州弟,500 是服務(wù)器錯(cuò)誤;
- 原因:作為數(shù)字狀態(tài)碼補(bǔ)充低零,是更詳細(xì)的解釋?字婆翔,幫助?理解原因。
看?下上?講? Wireshark 抓包?的響應(yīng)報(bào)?掏婶,狀態(tài)?是:
HTTP/1.1 200 OK\r\n
意思就是:“瀏覽器你好啃奴,我已經(jīng)處理完了你的請(qǐng)求,這個(gè)報(bào)?使?的協(xié)議版本號(hào)是 1.1雄妥,狀態(tài)碼是 200最蕾,?切 OK【グ牛”
?另?個(gè)“GET /favicon.ico HTTP/1.1”的響應(yīng)報(bào)?狀態(tài)?是:
HTTP/1.1 404 Not Found
翻譯成?話就是:“抱歉啊瀏覽器揖膜,剛才你的請(qǐng)求收到了誓沸,但我沒(méi)找到你要的資源梅桩,錯(cuò)誤代碼是 404,接下來(lái)的事情你就看著辦吧拜隧∷薨伲”
頭部字段
請(qǐng)求?或狀態(tài)?再加上頭部字段集合就構(gòu)成了 HTTP 報(bào)??完整的請(qǐng)求頭或響應(yīng)頭趁仙,我畫了兩個(gè)示意圖,你可以看?下垦页。
請(qǐng)求頭和響應(yīng)頭的結(jié)構(gòu)是基本?樣的雀费,唯?的區(qū)別是起始?,所以我把請(qǐng)求頭和響應(yīng)頭?的字段放在?起介紹痊焊。
頭部字段是 key-value 的形式盏袄,key 和 value 之間?“:”分隔,最后? CRLF 換?表示字段結(jié)束薄啥。?如在“Host: 127.0.0.1”這??? key 就是“Host”辕羽,value 就是“127.0.0.1”。
HTTP 頭字段?常靈活垄惧,不僅可以使?標(biāo)準(zhǔn)?的 Host刁愿、Connection 等已有頭,也可以任意添加?定義頭到逊,這就給 HTTP 協(xié)議帶來(lái)了?限的擴(kuò)展可能铣口。
不過(guò)使?頭字段需要注意下??點(diǎn):
- 字段名不區(qū)分??寫,例如“Host”也可以寫成“host”觉壶,但?字??寫的可讀性更好脑题;
- 字段名?不允許出現(xiàn)空格,可以使?連字符“-”铜靶,但不能使?下劃線“_”旭蠕。例如,“test-name”是合法的字段名旷坦,?“test name”“test_name”是不正確的字段名掏熬;
- 字段名后?必須緊接著“:”,不能有空格秒梅,?“:”后的字段值前可以有多個(gè)空格旗芬;
- 字段的順序是沒(méi)有意義的,可以任意排列不影響語(yǔ)義捆蜀;
- 字段原則上不能重復(fù)疮丛,除?這個(gè)字段本身的語(yǔ)義允許,例如 Set-Cookie辆它。
?wireshark抓包的分析
請(qǐng)求
響應(yīng)
常?頭字段
HTTP 協(xié)議規(guī)定了?常多的頭部字段誊薄,實(shí)現(xiàn)各種各樣的功能,但基本上可以分為四?類:
- 通?字段:在請(qǐng)求頭和響應(yīng)頭?都可以出現(xiàn)锰茉;
- 請(qǐng)求字段:僅能出現(xiàn)在請(qǐng)求頭?呢蔫,進(jìn)?步說(shuō)明請(qǐng)求信息或者額外的附加條件;
- 響應(yīng)字段:僅能出現(xiàn)在響應(yīng)頭?飒筑,補(bǔ)充說(shuō)明響應(yīng)報(bào)?的信息片吊;
- 實(shí)體字段:它實(shí)際上屬于通?字段绽昏,但專?描述 body 的額外信息。
對(duì) HTTP 報(bào)?的解析和處理實(shí)際上主要就是對(duì)頭字段的處理俏脊,理解了頭字段也就理解了 HTTP 報(bào)?全谤。
這?主要講?個(gè)最基本的頭,看完了它們你就應(yīng)該能夠讀懂?多數(shù) HTTP 報(bào)?了爷贫。
User-Agent
User-Agent是請(qǐng)求字段认然,只出現(xiàn)在請(qǐng)求頭?。它使??個(gè)字符串來(lái)描述發(fā)起 HTTP 請(qǐng)求的客戶端漫萄,服務(wù)器可以依據(jù)它來(lái)返回最合適此瀏覽器顯示的??季眷。
但由于歷史的原因,User-Agent ?尘砜瑁混亂子刮,每個(gè)瀏覽器都?稱是“Mozilla”“Chrome”“Safari”,企圖使?這個(gè)字段來(lái)互相“偽裝”窑睁,導(dǎo)致 User-Agent 變得越來(lái)越?挺峡,最終變得毫?意義。
不過(guò)有的?較“誠(chéng)實(shí)”的爬?會(huì)在 User-Agent ??“spider”標(biāo)明??是爬?担钮,所以可以利?這個(gè)字段實(shí)現(xiàn)簡(jiǎn)單的反爬?策略橱赠。
Accept
Accept是請(qǐng)求字段,代表客戶端希望接受的數(shù)據(jù)類型箫津。?如Accept:text/xml(application/json);代表客戶端希望接受的數(shù)據(jù)類型是xml(json )類型狭姨;?如Accept: /則說(shuō)明客戶端接收所有類型的數(shù)據(jù)。
Host
?先要說(shuō)的是Host字段苏遥,它屬于請(qǐng)求字段饼拍,只能出現(xiàn)在請(qǐng)求頭?,它同時(shí)也是唯??個(gè) HTTP/1.1 規(guī)范?要求必須出現(xiàn)的字段田炭,也就是說(shuō)师抄,如果請(qǐng)求頭?沒(méi)有 Host,那這就是?個(gè)錯(cuò)誤的報(bào)?教硫。
Host 字段告訴服務(wù)器這個(gè)請(qǐng)求應(yīng)該由哪個(gè)主機(jī)來(lái)處理叨吮,當(dāng)?臺(tái)計(jì)算機(jī)上托管了多個(gè)虛擬主機(jī)的時(shí)候,服務(wù)器端就需要? Host 字段來(lái)選擇瞬矩,有點(diǎn)像是?個(gè)簡(jiǎn)單的“路由重定向”茶鉴。
例如我們的試驗(yàn)環(huán)境,在 127.0.0.1 上有三個(gè)虛擬主機(jī):“www.chrono.com”“www.metroid.net”和“origin.io”景用。那么當(dāng)使?域名的?式訪問(wèn)時(shí)涵叮,就必須要?Host 字段來(lái)區(qū)分這三個(gè) IP 相同但域名不同的?站,否則服務(wù)器就會(huì)找不到合適的虛擬主機(jī),?法處理围肥。
Range
Range是請(qǐng)求字段剿干。
?如Range: bytes=5001-10000 對(duì)于只需獲資源的范圍請(qǐng)求蜂怎,包含?部字段 Range 即可告知服務(wù)器資源的指定范圍穆刻。上?的示例表示請(qǐng)求獲取從第 5001 字節(jié)到第 10000 字節(jié)的資源。
?如Range: bytes=0- 則是請(qǐng)求所有的數(shù)據(jù)杠步。
接收到附帶 Range ?部字段請(qǐng)求的服務(wù)器氢伟,會(huì)在處理請(qǐng)求之后返回狀態(tài)碼為 206 Partial Content 的響應(yīng)。?法處理該范圍請(qǐng)求時(shí)幽歼,則會(huì)返回狀態(tài)碼 200 OK 的響應(yīng)及全部資源朵锣。
Connection
管理持久連接
①: close 斷開(kāi)連接
Connection: close
HTTP/1.1版本的默認(rèn)連接都是持久連接。為此甸私,客戶端會(huì)在持久連接上連續(xù)發(fā)送請(qǐng)求诚些。當(dāng)服務(wù)器端想明確斷開(kāi)連接時(shí),則指定 Connection ?部字段的值為 close 皇型。
②: Keep-Alive 保持連接
Connection: keep-alive
HTTP/1.1 之前的版本的默認(rèn)連接都是?持久連接诬烹。為此,如果想在舊版本的HTTP協(xié)議上維持持續(xù)連接弃鸦,則需要指定 Connection ?部字段的值為 keep-alive 绞吁。
在客戶單發(fā)送請(qǐng)求給服務(wù)器時(shí),攜帶此參數(shù)和值唬格,服務(wù)器也會(huì)加上字段和值進(jìn)?返回響應(yīng)家破。
http是?個(gè)?狀態(tài)的?向連接的協(xié)議。
http?狀態(tài):?狀態(tài)協(xié)議是指http協(xié)議本身對(duì)于事務(wù)處理沒(méi)有記憶功能购岗,服務(wù)器不知道瀏覽器的狀態(tài)汰聋。通俗的即使你登錄了,去訪問(wèn)同?個(gè)?站的不同??喊积,服務(wù)器都不會(huì)知道你是誰(shuí)马僻,如果需要記錄登錄?戶的信息,?戶操作注服,?戶?為等數(shù)據(jù)需要使?cookie或session來(lái)存儲(chǔ)韭邓。
keep-alive:從HTTP/1.1起,瀏覽器默認(rèn)都開(kāi)啟了Keep-Alive溶弟,保持連接特性女淑,客戶端和服務(wù)器都能選擇隨時(shí)關(guān)閉連接,則請(qǐng)求頭中為connection:close辜御。簡(jiǎn)單地說(shuō)鸭你,當(dāng)?個(gè)??打開(kāi)完成后,客戶端和服務(wù)器之間?于傳輸HTTP數(shù)據(jù)的TCP連接不會(huì)關(guān)閉,如果客戶端再次訪問(wèn)這個(gè)服務(wù)器上的??袱巨,會(huì)繼續(xù)使?這?條已經(jīng)建?的TCP連接阁谆。但是Keep-Alive不會(huì)永久保持連接,它有?個(gè)保持時(shí)間愉老,可以在不同的服務(wù)器軟件(如Apache)中設(shè)定這個(gè)時(shí)間场绿。
誤解:?狀態(tài)不代表HTTP不能保持TCP連接,更不能代表HTTP使?的是UDP協(xié)議(?連接)嫉入。即使http在?狀態(tài)下焰盗,只要客戶端和服務(wù)器的頭部信息connection:keep-alive,則在有效期內(nèi)他們使?同?條TCP連接咒林。
Date
Date字段是?個(gè)通?字段熬拒,但通常出現(xiàn)在響應(yīng)頭?,表示 HTTP 報(bào)?創(chuàng)建的時(shí)間垫竞,客戶端可以使?這個(gè)時(shí)間再搭配其他字段決定緩存策略澎粟。
Server
Server字段是響應(yīng)字段,只能出現(xiàn)在響應(yīng)頭?欢瞪。它告訴客戶端當(dāng)前正在提供 Web 服務(wù)的軟件名稱和版本號(hào)活烙, Server 字段也不是必須要出現(xiàn)的,因?yàn)檫@會(huì)把服務(wù)器的?部分信息暴露給外界引有,如果這個(gè)版本恰好存在 bug瓣颅,那么?客就有可能利? bug 攻陷服務(wù)器。所以譬正,有的?站響應(yīng)頭?要么沒(méi)有這個(gè)字段宫补,要么就給出?個(gè)完全?關(guān)的描述信息。
?如 GitHub曾我,它的 Server 字段?就看不出是使?了 Apache 還是 Nginx粉怕,只是顯示為“GitHub.com”。
再?如srs流媒體服務(wù)器的響應(yīng) Server:SRS/3.0.141(OuXuli)\r\n
Content-Type
Content-Type是實(shí)體字段抒巢,表發(fā)送端(客戶端|服務(wù)器)發(fā)送的實(shí)體數(shù)據(jù)的數(shù)據(jù)類型贫贝。?如:ContentType:text/html(application/json) ; 代表發(fā)送端發(fā)送的數(shù)據(jù)格式是html(json)。
Content-Length
實(shí)體字段?要說(shuō)的?個(gè)是Content-Length蛉谜,它表示報(bào)?? body 的?度稚晚,也就是請(qǐng)求頭或響應(yīng)頭空?后?數(shù)據(jù)的?度。服務(wù)器看到這個(gè)字段型诚,就知道了后續(xù)有多少數(shù)據(jù)客燕,可以直接接收。如果沒(méi)有這個(gè)字段狰贯,那么 body 就是不定?的也搓,需要使? chunked ?式分段傳輸赏廓。