??大家好,我是白晨,一個(gè)不是很能熬夜??,但是也想日更的人?。如果喜歡這篇文章,點(diǎn)個(gè)贊??,關(guān)注一下??白晨吧!你的支持就是我最大的動(dòng)力!??????
??前言
喲,大家好,我是白晨。距離上一次更新已經(jīng)過(guò)了一段時(shí)間了,屬實(shí)是當(dāng)鴿子當(dāng)慣了??。
上一篇文章我們?nèi)嬷v解了網(wǎng)絡(luò)中的基礎(chǔ)知識(shí)(沒(méi)看過(guò)上篇文章的的可以先去看上一篇文章【網(wǎng)絡(luò)】網(wǎng)絡(luò)基礎(chǔ)知識(shí)詳解),這篇文章白晨準(zhǔn)備從應(yīng)用層中的一個(gè)及其重要的協(xié)議開(kāi)始講起,它就是——HTTP協(xié)議
。HTTP協(xié)議由于簡(jiǎn)單、快速而成為了應(yīng)用最廣泛的web文檔傳遞協(xié)議協(xié)議,在理解了HTTP協(xié)議的種種行為后,再自頂向下去學(xué)習(xí)傳輸層,網(wǎng)絡(luò)層以及數(shù)據(jù)鏈路層將會(huì)好理解許多。
??HTTP協(xié)議
??1. HTTP背景介紹
1989 年 3 月,HTTP 誕生。最初設(shè)想的基本理念是: 借助多文檔之間相互關(guān)聯(lián)形成的超文本(HyperText),連成可相互參閱的 WWW( World Wide Web,萬(wàn)維網(wǎng))?,F(xiàn)在已提出了 3 項(xiàng) WWW 構(gòu)建技術(shù),分別是:
- 把 SGML(StandardGeneralized Markup Language,標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言)作為頁(yè)面的文本標(biāo)記語(yǔ)言的 HTML(HyperText Markup Language,超文本標(biāo)記語(yǔ)言);
- 作為文檔傳遞協(xié)議的 HTTP ;
- 指定文檔所在地址的 URL(Uniform Resource Locator,統(tǒng)一資源定位符)。
WWW 這一名稱, 是 Web 瀏覽器當(dāng)年用來(lái)瀏覽超文本的客戶端應(yīng)用程序時(shí)的名稱。現(xiàn)在則用來(lái)表示這一系列的集合,也可簡(jiǎn)稱為 Web。
?2. HTTP知識(shí)預(yù)備
2.1 TCP/IP協(xié)議
上篇文章我們?cè)?jīng)講過(guò),TCP/IP模型將網(wǎng)絡(luò)分為了四層,將不同的任務(wù)劃分,每層只要完成自己的任務(wù)即可,完成了每層間的解耦,同時(shí)上層依賴下層的服務(wù)以實(shí)現(xiàn)自己的功能。HTTP是一個(gè)建立在TCP/IP協(xié)議族上的應(yīng)用層協(xié)議,所以,要利用HTTP發(fā)送數(shù)據(jù),就要通過(guò)上圖過(guò)程。
我們用 HTTP 舉例來(lái)說(shuō)明,
- 首先作為發(fā)送端的客戶端在應(yīng)用層(HTTP 協(xié)議)發(fā)出一個(gè)想看某個(gè) Web 頁(yè)面的 HTTP 請(qǐng)求。
- 接著,為了傳輸方便,在傳輸層(TCP 協(xié)議)把從應(yīng)用層處收到的數(shù)據(jù)(HTTP 請(qǐng)求報(bào)文)進(jìn)行分割,并在各個(gè)報(bào)文上打上標(biāo)記序號(hào)及端口號(hào)后轉(zhuǎn)發(fā)給網(wǎng)絡(luò)層。
- 在網(wǎng)絡(luò)層(IP 協(xié)議),增加作為通信目的地的 MAC 地址后轉(zhuǎn)發(fā)給鏈路層。這樣一來(lái),發(fā)往網(wǎng)絡(luò)的通信請(qǐng)求就準(zhǔn)備齊全了。
- 接收端的服務(wù)器在鏈路層接收到數(shù)據(jù), 按序往上層發(fā)送,一直到應(yīng)用層。當(dāng)傳輸?shù)綉?yīng)用層,才能算真正接收到由客戶端發(fā)送過(guò)來(lái)的 HTTP。
2.2 URI 和 URL
URL (Uniform Resource Locator)
,我們稱為統(tǒng)一資源定位符
。它是對(duì)可以從互聯(lián)網(wǎng)上得到的資源的位置和訪問(wèn)方法的一種簡(jiǎn)潔的表示,是互聯(lián)網(wǎng)上標(biāo)準(zhǔn)資源的地址。也就是我們經(jīng)常說(shuō)的網(wǎng)址。
- 具體格式為:
URI (Uniform Resource Identifier)
,我們稱為統(tǒng)一資源標(biāo)識(shí)符
。URI 就是由某個(gè)協(xié)議方案表示的資源的定位標(biāo)識(shí)符。
- URI 與 URL 的聯(lián)系:
URI 用字符串標(biāo)識(shí)某一互聯(lián)網(wǎng)資源, 而 URL 表示資源的地點(diǎn)(互聯(lián)網(wǎng)上所處的位置),可見(jiàn) URL 是 URI 的子集。URI 可以分為 URL + URN (統(tǒng)一資源名稱) ,打個(gè)比方,URI 相當(dāng)于一個(gè)人,URL 就是這個(gè)人的住址,URN 就是這個(gè)人的名字。
所以,URL 必定也是 URI。
- URI 與 URL 的區(qū)別:
URI 不一定是 URL,根據(jù)上文,URI 中還有一個(gè) URN ,它只是命名資源,而不定位資源。
2.3 DNS服務(wù)
DNS( Domain Name System)**DNS 協(xié)議提供通過(guò)域名查找 IP 地址,或逆向從 IP 地址反查域名的服務(wù) **,和 HTTP 協(xié)議一樣位于應(yīng)用層的協(xié)議。
相比IP地址這種純數(shù)字,域名這種字母+數(shù)字的組合讓人更加容易記憶,比如:百度的IP地址為
110.242.68.3
,域名為www.baicdu.com
??梢?jiàn),大多數(shù)用戶在訪問(wèn)網(wǎng)站時(shí),都習(xí)慣使用域名。但我們知道在網(wǎng)絡(luò)上,IP地址+端口號(hào)才能定位一個(gè)唯一的服務(wù),所以就需要 DNS服務(wù) 將域名解析為 IP地址。
具體工作場(chǎng)景如下圖:
??3. HTTP協(xié)議格式
請(qǐng)求報(bào)文
POST /data/index HTTP/1.1
Host: baichen.com
Connection: keep-alive
Content-Type: text/html; charset=utf8
Content-Length: 16
name=chen&age=37
請(qǐng)求報(bào)文的構(gòu)成為:
由此,我們可以得到請(qǐng)求報(bào)文的格式為:
-
請(qǐng)求行:
方法
+URL
+協(xié)議版本
-
請(qǐng)求頭部: 請(qǐng)求的屬性, 冒號(hào)分割的鍵值對(duì);每組屬性之間使用
\n
分隔。 -
空行:空行用于標(biāo)識(shí)請(qǐng)求頭部結(jié)束,請(qǐng)求正文開(kāi)始。
-
請(qǐng)求正文: 空行后面的內(nèi)容都是請(qǐng)求正文,內(nèi)容為應(yīng)發(fā)送的數(shù)據(jù)。請(qǐng)求正文允許為空字符串。如果請(qǐng)求正文存在, 則在請(qǐng)求頭部中會(huì)有一個(gè)Content-Length屬性來(lái)標(biāo)識(shí)請(qǐng)求正文的長(zhǎng)度。
這里要解釋一個(gè)問(wèn)題,為什么前文講解URL的結(jié)構(gòu)這么復(fù)雜,但是在上面的請(qǐng)求報(bào)文中卻好像只有目錄結(jié)構(gòu),而沒(méi)有協(xié)議、域名等部分?
當(dāng)客戶端請(qǐng)求訪問(wèn)資源而發(fā)送請(qǐng)求時(shí), URL 需要將作為請(qǐng)求報(bào)文中的請(qǐng)求 URL 包含在內(nèi)。指定請(qǐng)求 URI 的方式有很多。
- 指定完整的URL
GET http://www.baichen.com/index.html HTTP/1.1
- 在首部字段Host中寫(xiě)明網(wǎng)絡(luò)域名或IP地址
GET / HTTP/1.1 Host: www.baichen.com HTTP請(qǐng)求的'/'并不是根目錄,而是web根目錄,意味著我們要請(qǐng)求該網(wǎng)站的首頁(yè)
- 對(duì)于針對(duì)服務(wù)器本身的訪問(wèn),可以用* 來(lái)代替請(qǐng)求 URL
OPTIONS * HTTP/1.1 -- 查詢服務(wù)器支持的方法
響應(yīng)報(bào)文
HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 06:50:15 GMT
Content-Length: 362
Content-Type: text/html
<html>
…
響應(yīng)報(bào)文的構(gòu)成為:
由此,我們可以得到響應(yīng)報(bào)文的格式為:
- 相應(yīng)行:
協(xié)議版本
+狀態(tài)碼
+狀態(tài)碼描述
- 響應(yīng)頭部: 請(qǐng)求的屬性, 冒號(hào)分割的鍵值對(duì);每組屬性之間使用
\n
分隔。 - 空行:空行用于標(biāo)識(shí)響應(yīng)頭部結(jié)束,響應(yīng)正文開(kāi)始。
- 響應(yīng)正文: 空行后面的內(nèi)容都是響應(yīng)正文,內(nèi)容為應(yīng)發(fā)送的數(shù)據(jù)。響應(yīng)正文允許為空字符串。如果響應(yīng)正文存在,則在響應(yīng)頭部中會(huì)有一個(gè)Content-Length屬性來(lái)標(biāo)識(shí)響應(yīng)正文的長(zhǎng)度;如果服務(wù)器返回了一個(gè)html頁(yè)面,那么html頁(yè)面內(nèi)容就是在響應(yīng)正文中。
??4. HTTP的方法
向請(qǐng)求 URL 指定的資源發(fā)送請(qǐng)求報(bào)文時(shí),采用稱為方法的命令。方法的作用在于, 可以指定請(qǐng)求的資源按期望產(chǎn)生某種行為。方法中有
GET
、POST
和HEAD
等。eg.
- HTTP支持的方法詳見(jiàn)下表:
方法 | 說(shuō)明 | 支持的HTTP協(xié)議版本 |
---|---|---|
GET | 獲取資源 | 1.0、1.1 |
POST | 傳輸實(shí)體主體 | 1.0、1.1 |
PUT | 傳輸文件 | 1.0、1.1 |
HEAD | 獲得報(bào)文首部 | 1.0、1.1 |
DELETE | 刪除文件 | 1.0、1.1 |
OPTIONS | 詢問(wèn)支持方法 | 1.1 |
TRACE | 追蹤路徑 | 1.1 |
CONNECT | 要求用隧道協(xié)議連接代理 | 1.1 |
LINK | 建立和資源之間的聯(lián)系 | 1.0 |
UNLINE | 斷開(kāi)連接關(guān)系 | 1.0 |
LINK 和 UNLINK 方法已經(jīng)被 HTTP/1.1 棄用,這兩個(gè)方法只需了解即可。
GET:獲取資源
GET 方法用來(lái)請(qǐng)求訪問(wèn)已被 URL 識(shí)別的資源。指定的資源經(jīng)服務(wù)器端解析后返回響應(yīng)內(nèi)容。
請(qǐng)求報(bào)文:
GET / HTTP/1.1
Host: www.baichen.com
Connection: keep-alive
User-Agent: XXXXX
響應(yīng)報(bào)文:
HTTP/1.1 200 OK
Content-Type: text/plain
hello, i'm from the futurn
上面就是一個(gè)經(jīng)典的使用GET方法的例子,GET方法是一個(gè)使用非常頻繁的方法,用于獲取指定 URL 的資源,當(dāng)我們?cè)L問(wèn)一個(gè)網(wǎng)站的時(shí)候,第一次使用的方法基本都是GET,以獲取網(wǎng)頁(yè)信息。
POST:傳輸實(shí)體主體
POST 方法用來(lái)傳輸實(shí)體的主體。雖然用 GET 方法也可以傳輸實(shí)體的主體,但一般不用 GET 方法進(jìn)行傳輸,而是用 POST 方法。雖說(shuō) POST 的功能與 GET 很相似,但POST 的主要目的并不是獲取響應(yīng)的主體內(nèi)容。
注:使用GET方法傳輸?shù)膬?nèi)容直接會(huì)在URL上顯示,而用POST方法傳輸?shù)膬?nèi)容會(huì)在報(bào)文正文傳輸,而不會(huì)在URL上直接顯示。
POST / HTTP/1.1
Host: www.baichen.com
Content-Length: 1560(1560字節(jié)的數(shù)據(jù))
PUT:傳輸文件
PUT 方法用來(lái)傳輸文件。就像 FTP 協(xié)議的文件上傳一樣,要求在請(qǐng)求報(bào)文的主體中包含文件內(nèi)容,然后保存到請(qǐng)求 URL 指定的位置。 這個(gè)方法由于安全性較低,所以大部分主流網(wǎng)站不會(huì)支持這個(gè)方法或者配合驗(yàn)證機(jī)制使用此方法。
PUT / HTTP/1.1
Host: www.baichen.com
Content-Type: text/html
Content-Length: 1560(1560字節(jié)的數(shù)據(jù))
HEAD:獲得報(bào)文首部
HEAD 方法和 GET 方法一樣,只是不返回報(bào)文主體部分,用于確認(rèn) URL 的有效性及資源更新的日期時(shí)間等。
HEAD /index.html HTTP/1.1
Host: www.baichen.com
DELETE:刪除文件
DELETE 方法按請(qǐng)求 URL 刪除指定的資源。DELETE 方法和 PUT 效果正好相反,所以 DELETE 也具有安全性問(wèn)題,一般不會(huì)單獨(dú)使用。
DELETE /index.html HTTP/1.1
Host: www.baichen.com
OPTIONS:詢問(wèn)支持的方法
OPTIONS 方法用來(lái)查詢針對(duì)請(qǐng)求 URL 指定的資源支持的方法。
請(qǐng)求:
OPTIONS * HTTP/1.1
Host: www.baichen.com
返回:
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
(返回服務(wù)器支持的方法)
TRACE:追蹤路徑
TRACE 方法是讓 Web 服務(wù)器端將之前的請(qǐng)求通信環(huán)回給客戶端的方法 .客戶端通過(guò) TRACE 方法可以查詢發(fā)送出去的請(qǐng)求是怎樣被加工修改 / 篡改的。這是因?yàn)椋?qǐng)求想要連接到源目標(biāo)服務(wù)器可能會(huì)通過(guò)代理中轉(zhuǎn), TRACE 方法就是用來(lái)確認(rèn)連接過(guò)程中發(fā)生的一系列操作。
發(fā)送請(qǐng)求時(shí), 在
Max-Forwards
首部字段中填入數(shù)值,每經(jīng)過(guò)一個(gè)服務(wù)器端就將該數(shù)字減 1, 當(dāng)數(shù)值剛好減到 0 時(shí),就停止繼續(xù)傳輸,最后接收到請(qǐng)求的服務(wù)器端則返回狀態(tài)碼 200 OK 的響應(yīng)
請(qǐng)求:
TRACE / HTTP/1.1
Host: WWW.baichen.com
Max-Forwards: 2
返回:
HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 1024
TRACE / HTTP/1.1
Host: www.baichen.com
Max-Forwards: 2(返回響應(yīng)包含請(qǐng)求內(nèi)容)
CONNECT:要求用隧道協(xié)議連接代理
CONNECT 方法要求在與代理服務(wù)器通信時(shí)建立隧道,實(shí)現(xiàn)用隧道協(xié)議進(jìn)行 TCP 通信。
使用隧道通信相當(dāng)于是在客戶端和服務(wù)器之間建立了一個(gè)隧道,兩者的報(bào)文從隧道走,可以保證通信的連續(xù)和安全。
CONNECT方法使用格式:
CONNECT 代理服務(wù)器名:端口號(hào) HTTP版本
eg.
CONNECT 101.58.2.1:8080 HTTP/1.1
Host: www.baichen.com
??5. HTTP的狀態(tài)碼
狀態(tài)碼的職責(zé)是當(dāng)客戶端向服務(wù)器端發(fā)送請(qǐng)求時(shí),描述返回的請(qǐng)求結(jié)果。 借助狀態(tài)碼,用戶可以知道服務(wù)器端是正常處理了請(qǐng)求,還是出現(xiàn)了錯(cuò)誤。
eg.
5.1 狀態(tài)碼分類
狀態(tài)碼 | 類別 | 原因短語(yǔ) |
---|---|---|
1XX | Informational(信息性狀態(tài)碼) | 接收的請(qǐng)求正在處理 |
2XX | Success(成功狀態(tài)碼) | 請(qǐng)求正常處理完畢 |
3XX | Redirection(重定向狀態(tài)碼) | 需要進(jìn)行附加操作以完成請(qǐng)求 |
4XX | Client Error(客戶端錯(cuò)誤狀態(tài)碼) | 服務(wù)器無(wú)法處理請(qǐng)求 |
5XX | Server Error(服務(wù)器錯(cuò)誤狀態(tài)碼) | 服務(wù)器處理請(qǐng)求出錯(cuò) |
RFC2016上規(guī)定的狀態(tài)碼有40余種,再加上WebDAV等的狀態(tài)碼,一共有60余種。但是,常用的狀態(tài)碼大概就14種,下面由我向大家介紹這14種狀態(tài)碼。
5.2 2XX 成功
2XX
的響應(yīng)結(jié)果表明請(qǐng)求被正常處理了。
200 OK
表示從客戶端發(fā)來(lái)的請(qǐng)求在服務(wù)器端被正常處理了。
204 No Content
該狀態(tài)碼代表服務(wù)器接收的請(qǐng)求已成功處理,但在返回的響應(yīng)報(bào)文中不含實(shí)體的主體部分。 另外,也不允許返回任何實(shí)體的主體。比如,當(dāng)從瀏覽器發(fā)出請(qǐng)求處理后, 返回 204 響應(yīng),那么瀏覽器顯示的頁(yè)面不發(fā)生更新 。
206 Partial Content
該狀態(tài)碼表示客戶端進(jìn)行了范圍請(qǐng)求,而服務(wù)器成功執(zhí)行了這部分的
GET
請(qǐng)求。響應(yīng)報(bào)文中包含由Content-Range
首部字段指定范圍的實(shí)體內(nèi)容。
5.3 3XX 重定向
3XX
響應(yīng)結(jié)果表明瀏覽器需要執(zhí)行某些特殊的處理以正確處理請(qǐng)求。
301 Moved Permanently
永久性重定向。該狀態(tài)碼表示請(qǐng)求的資源已被分配了新的
URL
,以后應(yīng)使用資源現(xiàn)在所指的URL
。也就是說(shuō),如果已經(jīng)把資源對(duì)應(yīng)的URL
保存為書(shū)簽了,這時(shí)應(yīng)該按Location
首部字段提示的URL
重新保存。永久性重定向一般用于網(wǎng)址搬遷、網(wǎng)站升級(jí)等服務(wù),需要用
Location: 新的地址
指明跳轉(zhuǎn)的地址,301
狀態(tài)碼會(huì)建議瀏覽器更新書(shū)簽,將其指向新網(wǎng)址,但是瀏覽器的實(shí)現(xiàn)各不相同,可能需要你手動(dòng)修改,也可能幫你修改。
302 Found
臨時(shí)性重定向。該狀態(tài)碼表示請(qǐng)求的資源已被分配了新的
URL
,希望用戶(本次)能使用新的URL
訪問(wèn)。一般用于網(wǎng)站搶修,或者跳轉(zhuǎn)至登錄頁(yè)面等,這個(gè)狀態(tài)碼也需要配合
Location: 新的地址
一起使用,這個(gè)狀態(tài)碼不會(huì)建議瀏覽器更新書(shū)簽。
303 See Other
該狀態(tài)碼表示由于請(qǐng)求對(duì)應(yīng)的資源存在著另一個(gè)
URI
,應(yīng)使用GET
方法定向獲取請(qǐng)求的資源。303
狀態(tài)碼和302 Found
狀態(tài)碼有著相同的功能,但303
狀態(tài)碼明確表示客戶端應(yīng)當(dāng)采用GET
方法獲取資源,這點(diǎn)與302
狀態(tài)碼有區(qū)別。
注:當(dāng) 301、302、303 響應(yīng)狀態(tài)碼返回時(shí),幾乎所有的瀏覽器都會(huì)把 POST 改成 GET,并刪除請(qǐng)求報(bào)文內(nèi)的主體,之后請(qǐng)求會(huì)自動(dòng)再次發(fā)送。301、302 標(biāo)準(zhǔn)是禁止將 POST 方法改變成 GET 方法的,但實(shí)際使用時(shí)大家都會(huì)這么做。
304 Not Modified
該狀態(tài)碼表示客戶端發(fā)送附帶條件的請(qǐng)求 A 時(shí),服務(wù)器端允許請(qǐng)求訪問(wèn)資源,但未滿足條件的情況。
304
狀態(tài)碼返回時(shí),不包含任何響應(yīng)的主體部分。
307 Temporary Redirect
臨時(shí)重定向。該狀態(tài)碼與
302 Found
有著相同的含義。盡管302
標(biāo)準(zhǔn)禁止POST
變換成GET
,但實(shí)際使用時(shí)大家并不遵守。
307
會(huì)遵照瀏覽器標(biāo)準(zhǔn), 不會(huì)從POST
變成GET
。但是,對(duì)于處理響應(yīng)時(shí)的行為,每種瀏覽器有可能出現(xiàn)不同的情況。
5.4 4XX 客戶端錯(cuò)誤
4XX
的響應(yīng)結(jié)果表明客戶端是發(fā)生錯(cuò)誤的原因所在。
400 Bad Request
該狀態(tài)碼表示請(qǐng)求報(bào)文中存在語(yǔ)法錯(cuò)誤。當(dāng)錯(cuò)誤發(fā)生時(shí),需修改請(qǐng)求的內(nèi)容后再次發(fā)送請(qǐng)求。另外,瀏覽器會(huì)像
200 OK
一樣對(duì)待該狀態(tài)碼。
401 Unauthorized
該狀態(tài)碼表示發(fā)送的請(qǐng)求需要有通過(guò)
HTTP 認(rèn)證
( BASIC 認(rèn)證、DIGEST 認(rèn)證)的認(rèn)證信息。 另外若之前已進(jìn)行過(guò) 1 次請(qǐng)求,則表示用戶認(rèn)證失敗。返回含有
401
的響應(yīng)必須包含一個(gè)適用于被請(qǐng)求資源的WWW-Authenticate
首部用以質(zhì)詢( challenge)用戶信息。當(dāng)瀏覽器初次接收到401
響應(yīng),會(huì)彈出認(rèn)證用的對(duì)話窗口。
403 Forbidden
該狀態(tài)碼表明對(duì)請(qǐng)求資源的訪問(wèn)被服務(wù)器拒絕了。服務(wù)器端沒(méi)有必要給出拒絕的詳細(xì)理由, 但如果想作說(shuō)明的話,可以在實(shí)體的主體部分對(duì)原因進(jìn)行描述,這樣就能讓用戶看到了。
未獲得文件系統(tǒng)的訪問(wèn)授權(quán), 訪問(wèn)權(quán)限出現(xiàn)某些問(wèn)題(從未授權(quán)的發(fā)送源 IP 地址試圖訪問(wèn))等列舉的情況都可能是發(fā)生
403
的原因。
404 Not Found
該狀態(tài)碼表明服務(wù)器上無(wú)法找到請(qǐng)求的資源。除此之外,也可以在服務(wù)器端拒絕請(qǐng)求且不想說(shuō)明理由時(shí)使用。
5.5 5XX服務(wù)器錯(cuò)誤
5XX
的響應(yīng)結(jié)果表明服務(wù)器本身發(fā)生錯(cuò)誤。
500 Internal Server Error
該狀態(tài)碼表明服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤。
503 Service Unavailable
該狀態(tài)碼表明服務(wù)器暫時(shí)處于超負(fù)載或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。 如果事先得知解除以上狀況需要的時(shí)間,最好寫(xiě)入
Retry After
首部字段再返回給客戶端。
狀態(tài)碼的本意是用來(lái)快速獲取請(qǐng)求狀態(tài),但是在很多場(chǎng)景下,我們會(huì)發(fā)現(xiàn)狀態(tài)碼和出錯(cuò)的地方根本就對(duì)不上。歸根到底,狀態(tài)碼并不是一種強(qiáng)制性的命令,而是一種建議式的反饋,很多瀏覽器對(duì)于同一狀態(tài)碼的處理可能都千差萬(wàn)別,比如,404 狀態(tài)碼在谷歌瀏覽器上,如果返回有實(shí)體主體,那么谷歌瀏覽器并不會(huì)理會(huì) 404 狀態(tài)碼,而是顯示返回的實(shí)體主體的內(nèi)容,但是404 狀態(tài)碼在Edge瀏覽器上就會(huì)強(qiáng)制執(zhí)行 404 Not Fount。
雖然網(wǎng)絡(luò)上的狀態(tài)碼魚(yú)龍混雜,但是我們自己寫(xiě)的狀態(tài)碼一定要符合標(biāo)準(zhǔn),這樣有利于養(yǎng)成良好的代碼習(xí)慣和排查錯(cuò)誤。
??6. HTTP的首部
HTTP 協(xié)議的請(qǐng)求和響應(yīng)報(bào)文中必定包含 HTTP 首部。首部?jī)?nèi)容為客戶端和服務(wù)器分別處理請(qǐng)求和響應(yīng)提供所需要的信息。
HTTP 首部字段是構(gòu)成 HTTP 報(bào)文的要素之一。在客戶端與服務(wù)器之間以 HTTP 協(xié)議進(jìn)行通信的過(guò)程中,無(wú)論是請(qǐng)求還是響應(yīng)都會(huì)使用首部字段,它能起到傳遞額外重要信息的作用。使用首部字段是為了給瀏覽器和服務(wù)器提供報(bào)文主體大小、 所使用的語(yǔ)言、認(rèn)證信息等內(nèi)容。
eg.
HTTP 首部字段分類
- 請(qǐng)求報(bào)文
- 響應(yīng)報(bào)文
HTTP 首部字段結(jié)構(gòu)
首部字段名: 字段值
eg.
Content-Type: text/plain, charset=utf-8 -- 報(bào)文主體的對(duì)象類型,字體協(xié)議為utf-8協(xié)議
HTTP 首部字段類型
- 請(qǐng)求首部字段
- 響應(yīng)首部字段
- 通用首部字段
- 實(shí)體首部字段
HTTP 首部字段概覽
- 通用首部字段
- 請(qǐng)求首部字段
- 響應(yīng)首部字段
- 實(shí)體首部字段
Connection
HTTP/1.1 之前的 HTTP 版本的默認(rèn)連接都是非持久連接,也叫短連接。HTTP 協(xié)議是基于 TCP 協(xié)議的,所以要進(jìn)行一次會(huì)話必須要經(jīng)過(guò)建立連接、請(qǐng)求、響應(yīng)、斷開(kāi)連接的過(guò)程,這樣每進(jìn)行一次請(qǐng)求和響應(yīng)都要建立連接和斷開(kāi)連接,非常耗費(fèi)時(shí)間,這是由于以前傳輸?shù)亩际求w積不太大文本等文件,不需要持久連接。但是,現(xiàn)在由于網(wǎng)絡(luò)傳輸需求的迅速發(fā)展,持久連接已經(jīng)成為了 HTTP 連接的必然。
為此,如果想在舊版本的 HTTP 協(xié)議上維持持續(xù)連接,則需要指定
Connection
首部字段的值為Keep-Alive
。Connection: Keep-Alive
HTTP/1.1 版本的默認(rèn)連接都是持久連接,也叫長(zhǎng)連接。為此,客戶端會(huì)在持久連接上連續(xù)發(fā)送請(qǐng)求。 當(dāng)服務(wù)器端想明確斷開(kāi)連接時(shí), 則指定
Connection 首部字段的值為 Close。Connection: Close
Connection 字段還有控制不再轉(zhuǎn)發(fā)給代理的首部字段的功能,但是這里主要是講解長(zhǎng)短連接的概念
Cookie
Cookie 的工作機(jī)制是用戶識(shí)別及狀態(tài)管理。 Web 網(wǎng)站為了管理用戶的狀態(tài)會(huì)通過(guò) Web 瀏覽器,把一些數(shù)據(jù)臨時(shí)寫(xiě)入用戶的計(jì)算機(jī)內(nèi)。接著當(dāng)用戶訪問(wèn)該Web網(wǎng)站時(shí),可通過(guò)通信方式取回之前發(fā)放的Cookie。
Cookie其實(shí)是一種特別我們經(jīng)常使用的字段,因?yàn)?strong>HTTP協(xié)議是一種無(wú)狀態(tài)的協(xié)議,也就意味著:本次會(huì)話和上一次會(huì)話之間沒(méi)有任何關(guān)系,互不影響。但是,我們?nèi)粘J褂肂站等視頻網(wǎng)站時(shí),卻能夠保持登錄狀態(tài),這是因?yàn)镠TTP協(xié)議不是無(wú)狀態(tài)的嗎?
當(dāng)然不是,HTTP協(xié)議引入了 Cookie 值作為“會(huì)話保持”的媒介,那么 cookie 到底是什么呢?
簡(jiǎn)單來(lái)說(shuō),Cookie 是一種保存在內(nèi)存或者本地磁盤上的數(shù)據(jù)文件,其中保存著客戶端的各種信息。
- Set-Cookie
當(dāng)服務(wù)器準(zhǔn)備開(kāi)始管理客戶端的狀態(tài)時(shí),會(huì)使用
Set-Cookie
字段事先設(shè)置客戶端的Cookie
。
Set-Cookie 字段的屬性:
屬性 | 說(shuō)明 |
---|---|
NAME=VALUE | 賦予Cookie的名稱和其值(必需項(xiàng)) |
expires=DATE | Cookie的有效期(若不明確指定則默認(rèn)為瀏覽器關(guān)閉前為止) |
path=PATH | 將服務(wù)器上的文件目錄作為Cookie的適用對(duì)象(若不指定則 默認(rèn)為文檔所在的文件目錄) |
domain=域名 | 作為Cookie適用對(duì)象的域名 (若不指定則默認(rèn)為創(chuàng)建 Cookie 的服務(wù)器的域名) |
Secure | 僅在HTTPS安全通信時(shí)才會(huì)發(fā)送Cookie |
HttpOnly | 加以限制,使Cookie不能被JavaScript腳本訪問(wèn) |
eg.
Set-Cookie: id=1; password=123456; expires=Tue, 05 Jul 2023 18:43:32 GMT; path=/; domain=baichen.com;
- Cookie
當(dāng)被服務(wù)器用
Set-Cookie
設(shè)置了Cookie
以后,客戶端發(fā)送請(qǐng)求報(bào)文時(shí),會(huì)在頭部字段添加一個(gè)Cookie
字段,用來(lái)保存服務(wù)器設(shè)置的Cookie
。接收到多個(gè)Cookie
時(shí),同樣可以以多個(gè)Cookie
形式發(fā)送。一般來(lái)說(shuō),每個(gè)
Cookie
的大小不能超過(guò)4KB
。
eg.
Cookie: id=1; password=123456;
- Set-Cookie 和 Cookie 實(shí)例
本會(huì)話的源代碼見(jiàn)簡(jiǎn)易HTTP服務(wù)器,以下報(bào)文都是基于下文代碼獲得,大家可以自行驗(yàn)證。
請(qǐng)求:
GET / HTTP/1.1
Host: baichen.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: xxx
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
響應(yīng):
HTTP/1.0 200 OK
Set-Cookie: id=1111 -- 設(shè)置Cookie
Set-Cookie: password=2222 -- 設(shè)置Cookie
Content-Type: text/html, charset=utf-8
Content-Length: XXX
.....
請(qǐng)求:
POST / HTTP/1.1
Host: baichen.com
Connection: keep-alive
Content-Length: 37
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: id=1111; password=2222 --- Cookie值
name=%E7%99%BD%E6%99%A8&passwd=123456
Session
如果有人通過(guò)一些手段截取了我們的
Cookie
文件,如果Cookie使用的是明文傳遞,那么我們的賬號(hào)、密碼等個(gè)人信息會(huì)被竊取,如果Cookie使用的是加密傳遞,雖然攻擊者不能獲得我們的私密信息,但是他可以使用我們的Cookie訪問(wèn)各種私密資源,甚至做違法之事。當(dāng)然,前文提到了Cookie在配置良好的情況下,安全性還不錯(cuò),但是總有被劫持的風(fēng)險(xiǎn)。
Session
就是為了解決上述問(wèn)題而設(shè)計(jì)的方案,具體做法是:存儲(chǔ)于服務(wù)器上的 Session 對(duì)象存儲(chǔ)特定用戶會(huì)話所需的屬性及配置等私密信息,第一次訪問(wèn)該服務(wù)器的用戶會(huì)創(chuàng)建一個(gè)唯一的 Session 對(duì)象存儲(chǔ)私密信息,以后訪問(wèn)該服務(wù)器時(shí)就根據(jù)用戶信息讀取對(duì)應(yīng)的信息。由于 Session 對(duì)象存儲(chǔ)在服務(wù)器上,不在客戶端本地保存,所以不會(huì)被劫持,這樣就解決了安全的問(wèn)題。但是可以只使用 Session 方案嗎?是不行的,Session 是需要消耗服務(wù)器服務(wù)器資源的,會(huì)增加運(yùn)行成本,所以并不能將大規(guī)模數(shù)據(jù)都保存到 Session 對(duì)象中。一般的方案是:Cookie 和 Session 配合使用,以達(dá)到安全和會(huì)話保持的平衡。
-
Cookie 和 Session 對(duì)比
-
Cookie
數(shù)據(jù)存放在客戶端上,Session
數(shù)據(jù)放在服務(wù)器上。 -
Cookie
不是很安全,別人可以分析存放在本地的Cookie
并進(jìn)行Cookie
欺騙。 -
Session
對(duì)比Cookie
來(lái)說(shuō)更安全,Session
會(huì)在一定時(shí)間內(nèi)保存在服務(wù)器上,但是當(dāng)訪問(wèn)增多,會(huì)比較占用服務(wù)器的性能。 - 一般的方案是:
Cookie
和Session
配合使用,以達(dá)到安全和會(huì)話保持的平衡。
-
??7. 搭建簡(jiǎn)易HTTP服務(wù)器
這次實(shí)現(xiàn)的HTTP服務(wù)器是一個(gè)最簡(jiǎn)單的HTTP服務(wù)器,主要是為了讓大家了解HTTP的使用,實(shí)現(xiàn)的功能有顯示網(wǎng)頁(yè),上傳數(shù)據(jù),設(shè)置Cookie等。
- 效果圖如下:
![]()
- 首先,封裝一個(gè)套接字庫(kù)。
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
std::cerr << "socket failed" << std::endl;
exit(1);
}
}
static void Bind(int sock, uint16_t port)
{
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
std::cerr << "bind failed" << std::endl;
exit(2);
}
}
static void Listen(int sock)
{
const int backlog = 5;
if(listen(sock, backlog) < 0)
{
std::cerr << "listen failed" << std::endl;
exit(3);
}
}
// tcp通信直接在套接字中寫(xiě)就可以,所以可以不用返回客戶端的信息
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(sock, (struct sockaddr*)&peer, &len);
if(new_sock < 0)
{
std::cerr << "accept failed" << std::endl;
exit(4);
}
return new_sock;
}
static void Connect(int sock, const std::string& ip, uint16_t port)
{
struct sockaddr_in server;
memset((void*)&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
{
std::cerr << "connect failed" << std::endl;
exit(5);
}
else
{
std::cout << "connect succeed" << std::endl;
}
}
};
- HTTP服務(wù):
#include "Sock.hpp"
#include <fstream>
#include <sys/stat.h>
// 設(shè)置網(wǎng)頁(yè)根目錄
#define WWWROOT "./wwwroot/"
#define HOME_PAGE "index.html"
void Usage(std::string args)
{
std::cout << "Usage : " << args << " server_port" << std::endl;
}
void *handler(void *args)
{
pthread_detach(pthread_self());
int sock = *(int *)args;
delete (int *)args;
#define NUM 1024 * 10
char buf[NUM];
memset(buf, 0, sizeof(NUM));
// 接收請(qǐng)求報(bào)文
ssize_t sz = recv(sock, buf, sizeof(buf) - 1, 0);
if (sz > 0)
{
buf[sz] = 0;
std::cout << buf << std::endl;
std::string fileloc = WWWROOT;
fileloc += HOME_PAGE;
// fileloc += "a/b";
// 讀取網(wǎng)頁(yè)信息
std::ifstream ifs(fileloc);
if (!ifs.is_open())
{
std::string http_response = "HTTP/1.0 404 NotFount";
http_response += "Content-Type: text/html, charset=utf-8";
http_response += "\n";
http_response += "<html><p> 你訪問(wèn)的資源不存在 </p></html>";
send(sock, http_response.c_str(), http_response.size(), 0);
}
else
{
// 構(gòu)建響應(yīng)報(bào)文
std::string line;
std::string echo;
while (std::getline(ifs, line))
{
echo += line;
}
struct stat fst;
stat(fileloc.c_str(), &fst);
std::string http_response = "HTTP/1.0 200 OK\n";
// std::string http_response = "HTTP/1.0 404 NotFount";
// std::string http_response = "HTTP/1.0 302 Temporarily Move\n";
// http_response += "Location: http://www.bilibili.com";
http_response += "Set-Cookie: id=1111\n";
http_response += "Set-Cookie: password=2222\n";
http_response += "Content-Type: text/html, charset=utf-8\n";
http_response += "Content-Length: ";
http_response += std::to_string(fst.st_size);
http_response += "\n\n";
http_response += echo;
// http_response += "\n";
http_response += "\n";
// std::string http_response = "HTTP/1.0 200 OK\n";
// http_response += "Content-Type: text/plain\n";
// http_response += "\n";
// http_response += "hello, i'm from the futurn\n";
// 發(fā)送響應(yīng)報(bào)文
send(sock, http_response.c_str(), http_response.size(), 0);
}
}
close(sock);
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(-1);
}
int sock = Sock::Socket();
Sock::Bind(sock, atoi(argv[1]));
Sock::Listen(sock);
while (true)
{
pthread_t tid;
int new_sock = Sock::Accept(sock);
// int *psock = new int(new_sock);
// pthread_create(&tid, nullptr, handler, psock);
}
return 0;
}
- index網(wǎng)頁(yè)構(gòu)建:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h5>hello, i'm baichen!</h5>
<h5>hello, 我是表單!</h5>
<!-- action是數(shù)據(jù)提交到哪 method是提交數(shù)據(jù)時(shí)使用什么方法,可自行測(cè)試GET、POST方法傳遞信息的不同 -->
<form action="/a/handler_form" method="POST">
姓名:<input type="text" name="name"><br/>
密碼:<input type="password" name="passwd"><br/>
<input type="submit" value="登錄">
</form>
</body>
</html>
??后記
參考書(shū)籍:《圖解HTTP》
本篇文章白晨講解了HTTP
的背景知識(shí)、協(xié)議結(jié)構(gòu)、狀態(tài)碼、相關(guān)技術(shù)知識(shí)以及服務(wù)器的編寫(xiě),也算是講解的非常全面了。其中也有一些知識(shí)由于篇幅問(wèn)題,白晨并沒(méi)有展開(kāi)說(shuō)明,比如HTTP的首部字段等,需要大家在這篇文章的基礎(chǔ)上自行進(jìn)行相關(guān)學(xué)習(xí)。下一篇文章,白晨準(zhǔn)備講解HTTP協(xié)議的升級(jí)版——HTTPS
協(xié)議,通過(guò)學(xué)習(xí)HTTPS的學(xué)習(xí),相信你會(huì)對(duì)HTTP協(xié)議有更深的理解。
如果大家有什么想和白晨交流的,歡迎私信白晨??。
如果講解有不對(duì)之處還請(qǐng)指正,我會(huì)盡快修改,多謝大家的包容。
如果大家喜歡這個(gè)系列,還請(qǐng)大家多多支持啦??!
如果這篇文章有幫到你,還請(qǐng)給我一個(gè)大拇指
??和小星星
??支持一下白晨吧!喜歡白晨【網(wǎng)絡(luò)】系列的話,不如關(guān)注
??白晨,以便看到最新更新喲!?。?mark hidden color="red">文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-784227.html
我是不太能熬夜的白晨,我們下篇文章見(jiàn)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-784227.html
到了這里,關(guān)于【網(wǎng)絡(luò)】HTTP協(xié)議詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!