偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

HTTP 協(xié)議中的 Transfer-Encoding

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
Transfer-Encoding,是一個(gè) HTTP 頭部字段,字面意思是「傳輸編碼」。實(shí)際上,HTTP 協(xié)議中還有另外一個(gè)頭部與編碼有關(guān):Content-Encoding(內(nèi)容編碼)。

Transfer-Encoding,是一個(gè) HTTP 頭部字段,字面意思是「傳輸編碼」。實(shí)際上,HTTP 協(xié)議中還有另外一個(gè)頭部與編碼有關(guān):Content-Encoding(內(nèi)容編碼)。Content-Encoding 通常用于對(duì)實(shí)體內(nèi)容進(jìn)行壓縮編碼,目的是優(yōu)化傳輸,例如用 gzip 壓縮文本文件,能大幅減小體積。內(nèi)容編碼通常是選擇性的,例如 jpg / png 這類文件一般不開啟,因?yàn)閳D片格式已經(jīng)是高度壓縮過的,再壓一遍沒什么效果不說還浪費(fèi) CPU。

而 Transfer-Encoding 則是用來改變報(bào)文格式,它不但不會(huì)減少實(shí)體內(nèi)容傳輸大小,甚至還會(huì)使傳輸變大,那它的作用是什么呢?本文接下來主要就是講這個(gè)。我們先記住一點(diǎn),Content-Encoding 和 Transfer-Encoding 二者是相輔相成的,對(duì)于一個(gè) HTTP 報(bào)文,很可能同時(shí)進(jìn)行了內(nèi)容編碼和傳輸編碼。

 [[148924]]

Persistent Connection

暫時(shí)把 Transfer-Encoding 放一邊,我們來看 HTTP 協(xié)議中另外一個(gè)重要概念:Persistent Connection(持久連接,通俗說法長連接)。我們知道 HTTP 運(yùn)行在 TCP 連接之上,自然也有著跟 TCP 一樣的三次握手、慢啟動(dòng)等特性,為了盡可能的提高 HTTP 性能,使用持久連接就顯得尤為重要了。為此,HTTP 協(xié)議引入了相應(yīng)的機(jī)制。

HTTP/1.0 的持久連接機(jī)制是后來才引入的,通過 Connection: keep-alive 這個(gè)頭部來實(shí)現(xiàn),服務(wù)端和客戶端都可以使用它告訴對(duì)方在發(fā)送完數(shù)據(jù)之后不需要斷開 TCP 連接,以備后用。HTTP/1.1 則規(guī)定所有連接都必須是持久的,除非顯式地在頭部加上 Connection: close。所以實(shí)際上,HTTP/1.1 中 Connection 這個(gè)頭部字段已經(jīng)沒有 keep-alive 這個(gè)取值了,但由于歷史原因,很多 Web Server 和瀏覽器,還是保留著給 HTTP/1.1 長連接發(fā)送 Connection: keep-alive 的習(xí)慣。

瀏覽器重用已經(jīng)打開的空閑持久連接,可以避開緩慢的三次握手,還可以避免遇上 TCP 慢啟動(dòng)的擁塞適應(yīng)階段,聽起來十分美妙。為了深入研究持久連接的特性,我決定用 Node 寫一個(gè)最簡單的 Web Server 用于測試,Node 提供了 http 模塊用于快速創(chuàng)建 HTTP Web Server,但我需要更多的控制,所以用 net 模塊創(chuàng)建了一個(gè) TCP Server:

JSrequire('net').createServer(function(sock) {
sock.on('data', function(data) {
sock.write('HTTP/1.1 200 OK\r\n');
sock.write('\r\n');
sock.write('hello world!');
sock.destroy();
});
}).listen(9090, '127.0.0.1');

啟動(dòng)服務(wù)后,在瀏覽器里訪問 127.0.0.1:9090,正確輸出了指定內(nèi)容,一切正常。去掉 sock.destroy() 這一行,讓它變成持久連接,重啟服務(wù)后再訪問一下。這次的結(jié)果就有點(diǎn)奇怪了:遲遲看不到輸出,通過 Network 查看請(qǐng)求狀態(tài),一直是 pending。

這是因?yàn)?,?duì)于非持久連接,瀏覽器可以通過連接是否關(guān)閉來界定請(qǐng)求或響應(yīng)實(shí)體的邊界;而對(duì)于持久連接,這種方法顯然不奏效。上例中,盡管我已經(jīng)發(fā)送完所有數(shù)據(jù),但瀏覽器并不知道這一點(diǎn),它無法得知這個(gè)打開的連接上是否還會(huì)有新數(shù)據(jù)進(jìn)來,只能傻傻地等了。

Content-Length

要解決上面這個(gè)問題,最容易想到的辦法就是計(jì)算實(shí)體長度,并通過頭部告訴對(duì)方。這就要用到 Content-Length 了,改造一下上面的例子:

JSrequire('net').createServer(function(sock) {
sock.on('data', function(data) {
sock.write('HTTP/1.1 200 OK\r\n');
sock.write('Content-Length: 12\r\n');
sock.write('\r\n');
sock.write('hello world!');
});
}).listen(9090, '127.0.0.1');

可以看到,這次發(fā)送完數(shù)據(jù)并沒有關(guān)閉 TCP 連接,但瀏覽器能正常輸出內(nèi)容并結(jié)束請(qǐng)求,因?yàn)闉g覽器可以通過 Content-Length 的長度信息,判斷出響應(yīng)實(shí)體已結(jié)束。那如果 Content-Length 和實(shí)體實(shí)際長度不一致會(huì)怎樣?有興趣的同學(xué)可以自己試試,通常如果 Content-Length 比實(shí)際長度短,會(huì)造成內(nèi)容被截?cái)?如果比實(shí)體內(nèi)容長,會(huì)造成 pending。

由于 Content-Length 字段必須真實(shí)反映實(shí)體長度,但實(shí)際應(yīng)用中,有些時(shí)候?qū)嶓w長度并沒那么好獲得,例如實(shí)體來自于網(wǎng)絡(luò)文件,或者由動(dòng)態(tài)語言生成。這時(shí)候要想準(zhǔn)確獲取長度,只能開一個(gè)足夠大的 buffer,等內(nèi)容全部生成好再計(jì)算。但這樣做一方面需要更大的內(nèi)存開銷,另一方面也會(huì)讓客戶端等更久。

我們?cè)谧?WEB 性能優(yōu)化時(shí),有一個(gè)重要的指標(biāo)叫 TTFB(Time To First Byte),它代表的是從客戶端發(fā)出請(qǐng)求到收到響應(yīng)的第一個(gè)字節(jié)所花費(fèi)的時(shí)間。大部分瀏覽器自帶的 Network 面板都可以看到這個(gè)指標(biāo),越短的 TTFB 意味著用戶可以越早看到頁面內(nèi)容,體驗(yàn)越好??上攵?,服務(wù)端為了計(jì)算響應(yīng)實(shí)體長度而緩存所有內(nèi)容,跟更短的 TTFB 理念背道而馳。但在 HTTP 報(bào)文中,實(shí)體一定要在頭部之后,順序不能顛倒,為此我們需要一個(gè)新的機(jī)制:不依賴頭部的長度信息,也能知道實(shí)體的邊界。

Transfer-Encoding: chunked

本文主角終于再次出現(xiàn)了,Transfer-Encoding 正是用來解決上面這個(gè)問題的。歷史上 Transfer-Encoding 可以有多種取值,為此還引入了一個(gè)名為 TE 的頭部用來協(xié)商采用何種傳輸編碼。但是最新的 HTTP 規(guī)范里,只定義了一種編碼傳輸:分塊編碼(chunked)。

分塊編碼相當(dāng)簡單,在頭部加入 Transfer-Encoding: chunked 之后,就代表這個(gè)報(bào)文采用了分塊編碼。這時(shí),報(bào)文中的實(shí)體需要改為用一系列分塊來傳輸。每個(gè)分塊包含十六進(jìn)制的長度值和數(shù)據(jù),長度值獨(dú)占一行,長度不包括它結(jié)尾的 CRLF(\r\n),也不包括分塊數(shù)據(jù)結(jié)尾的 CRLF。最后一個(gè)分塊長度值必須為 0,對(duì)應(yīng)的分塊數(shù)據(jù)沒有內(nèi)容,表示實(shí)體結(jié)束。按照這個(gè)格式改造下之前的代碼:

JSrequire('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');

上面這個(gè)例子中,我在響應(yīng)頭中表明接下來的實(shí)體會(huì)采用分塊編碼,然后輸出了 11 字節(jié)的內(nèi)容,接著又輸出了 5 字節(jié)內(nèi)容,最后用一個(gè) 0 長度的分塊表明數(shù)據(jù)已經(jīng)傳完了。用瀏覽器訪問這個(gè)服務(wù),可以得到正確結(jié)果??梢钥吹?,通過這種簡單的分塊策略,很好的解決了前面提出的問題。

前面說過 Content-Encoding 和 Transfer-Encoding 二者經(jīng)常會(huì)結(jié)合來用,其實(shí)就是針對(duì) Transfer-Encoding 的分塊再進(jìn)行 Content-Encoding。下面是我用 telnet 請(qǐng)求測試頁面得到的響應(yīng),就對(duì)分塊內(nèi)容進(jìn)行了 gzip 編碼:

SHELL> telnet 106.187.88.156 80
GET /test.php HTTP/1.1
Host: qgy18.imququ.com
Accept-Encoding: gzip
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 03 May 2015 17:25:23 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip
1f
�H���W(�/�I�J
0

用 HTTP 抓包神器 Fiddler 也可以看到類似結(jié)果,有興趣的同學(xué)可以自己試一下。

責(zé)任編輯:何妍 來源: Jerry Qu的小站
相關(guān)推薦

2015-09-15 13:48:01

網(wǎng)絡(luò)協(xié)議HTTP Client

2009-07-23 16:20:48

HTTP協(xié)議ASP.NET

2014-10-22 09:36:41

TCPIP

2015-09-17 10:29:07

Http網(wǎng)絡(luò)協(xié)議X-Forwarded

2015-09-16 13:11:52

Http網(wǎng)絡(luò)協(xié)議Proxy-Conne

2015-10-09 15:07:02

HTTP網(wǎng)絡(luò)協(xié)議

2017-05-26 10:35:13

前端HTTP

2019-10-11 08:51:11

Http協(xié)議Dubbo

2019-11-25 11:04:22

Http協(xié)議Dubbo

2011-08-09 14:08:51

iPhoneHTTP請(qǐng)求協(xié)議

2015-09-16 09:50:35

HTTP 網(wǎng)絡(luò)協(xié)議響應(yīng)頭

2010-06-08 12:31:15

HTTP協(xié)議實(shí)體

2010-07-01 16:01:05

HTTP協(xié)議

2020-06-17 21:39:11

HTTP協(xié)議服務(wù)器

2022-03-09 18:54:30

HTTP緩存協(xié)議cache

2023-10-24 09:07:14

CookieSessionHTTP

2019-08-23 06:36:32

2011-08-17 10:16:35

iPhone應(yīng)用HTTP請(qǐng)求協(xié)議

2015-01-16 09:22:54

2010-06-08 12:37:25

HTTP協(xié)議工作原理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)