溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Http協(xié)議Content-Length實例分析

發(fā)布時間:2022-03-23 14:12:50 來源:億速云 閱讀:180 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“Http協(xié)議Content-Length實例分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Http協(xié)議Content-Length實例分析”吧!

Http協(xié)議Content-Length詳解

問題

我們的手機App在做更新時會從服務(wù)器上下載的一些資源,一般都是一些小文件,更新的代碼差不多是下面這樣的:

static void update() throws IOException {
    URL url = new URL("http://172.16.59.129:8000/update/test.so");
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    if(conn.getResponseCode() == 200) {
        int totalLength = conn.getContentLength();
    BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
    byte[] buffer = new byte[512];
    int readLength = 0;
    int length = 0;
    while((length=in.read(buffer)) != -1) {
        readLength += length;
        //進度條
        System.out.println(((float)readLength) /((float)(totalLength)));
    }
    }
}

比如上面的代碼更新一個so文件,先通過content-length獲取文件的總大小,然后讀Stream,每讀一段,就計算出當(dāng)前讀的總大小,除以content-length,用來顯示進度條。

結(jié)果weblogic從10升級到12后,content-length一直返回-1,這樣就不能顯示進度條了,但是文件流還能正常讀。把weblogic重啟了,一開始還能返回content-length,一會又是-1了。

原因分析

Http協(xié)議的請求報文和回復(fù)報文都有header和body,body就是你要獲取的資源,例如一個html頁面,一個jpeg圖片,而header是用來做某些約定的。例如客戶端與服務(wù)端商定一些傳輸格式,客戶端先獲取頭部,得知一些格式信息,然后才開始讀取body。

客戶端: Accept-Encoding:gzip (給我壓縮一下,我用的是流量,先下載下來我再慢慢解壓吧)

服務(wù)端1:Content-Encoding:null(沒有Content-Encoding頭。 我不給壓縮,CPU沒空,你愛要不要)

服務(wù)端2:Content-Encoding:gzip (給你節(jié)省流量,壓縮一下)

客戶端:Connection: keep-alive (大哥,咱好不容易建了個TCP連接,下次接著用)

服務(wù)端1: Connection: keep-alive (都不容易,接著用)

服務(wù)端2: Connection: close (誰跟你接著用,我們這個TCP是一次性的,下次再找我還得重新連)

http協(xié)議沒有三次握手,一般客戶端向服務(wù)端請求資源時,以服務(wù)端為準(zhǔn)。還有一些header并沒有協(xié)商的過程,而是服務(wù)端直接告訴客戶端按什么來。例如上述的Content-Length,是服務(wù)端告訴客戶端body的大小有多大。但是!服務(wù)端并不一定能準(zhǔn)確的提前告訴你body有多大。服務(wù)端要先寫header,再寫body,如果要在header里把body大小寫進去,就得提前知道body大小。如果這個body是動態(tài)生成的,服務(wù)端先生成完,再開始寫header,這樣需要很多額外的開銷,所以header里不一定有content-length。

那客戶端怎么知道body的大小呢?服務(wù)器有三種方式告訴你。

1.服務(wù)器已經(jīng)知道資源大小,通過content-length這個header告訴你。

Content-Length:1076(body的大小是1076B,你讀取1076B就可以完成任務(wù)了)
Transfer-Encoding: null

2.服務(wù)器沒法提前知道資源的大小,或者不愿意花費資源提前計算資源大小,就會把http回復(fù)報文中加一個header叫Transfer-Encoding:chunked,就是分塊傳輸?shù)囊馑?。每一塊都使用固定的格式,前邊是塊的大小,后面是數(shù)據(jù),然后最后一塊大小是0。這樣客戶端解析的時候就需要注意去掉一些無用的字段。

Content-Length:null
Transfer-Encoding:chunked (接下來的body我要一塊一塊的傳,每一塊開始是這一塊的大小,等我傳到大小為0的塊時,就沒了)

3.服務(wù)器不知道資源的大小,同時也不支持chunked的傳輸模式,那么就既沒有content-length頭,也沒有transfer-encoding頭,這種情況下必須使用短連接,以連接結(jié)束來標(biāo)示數(shù)據(jù)傳輸結(jié)束,傳輸結(jié)束就能知道大小了。這時候服務(wù)器返回的header里Connection一定是close。

Content-Length:null

Transfer-Encoding:null

Connection:close(我不知道大小,我也用不了chunked,啥時候我關(guān)了tcp連接,就說明傳輸結(jié)束了)

實驗

我通過nginx在虛擬機里做實驗,默認(rèn)nginx是支持chunked模式的,可以關(guān)掉。

使用的代碼如下,可能會調(diào)整參數(shù)。

static void update() throws IOException {
    URL url = new URL("http://172.16.59.129:8000/update/test.so");
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    //conn.setRequestProperty("Accept-Encoding", "gzip");
    //conn.setRequestProperty("Connection", "keep-alive");
    conn.connect();
    if(conn.getResponseCode() == 200) {
        System.out.println(conn.getHeaderFields().keySet());
        System.out.println(conn.getHeaderField("transfer-encoding"));
        System.out.println(conn.getHeaderField("Content-Length"));
        System.out.println(conn.getHeaderField("Content-Encoding"));
        System.out.println(conn.getHeaderField("Connection"));
    }
}

1.nginx在開啟chunked_transfer_encoding的時候

(1) 在reqeust header里不使用gzip,也就是不加accept-encoding:gzip

test.so文件大小 結(jié)果
100B 能正常返回content-length,沒有transfer-encoding頭
69M 能正常返回content-length,沒有transfer-encoding頭
3072M 能正常返回content-length,沒有transfer-encoding頭

可以發(fā)現(xiàn)nginx不管資源多大,如果客戶端不接受gzip的壓縮格式,就不會使用chunked模式,而且跟是否使用短連接沒關(guān)系。

(2)在request header里加入gzip,accepting-encoding:gzip

test.so文件大小 結(jié)果
100B 沒有content-length,transfer-encoding=trunked
69M 沒有content-length,transfer-encoding=trunked
3072M 沒有content-length,transfer-encoding=trunked

可以看到nginx在開啟chunked_transfer_encoding,并且客戶端接受gzip的時候,會使用chunked模式,nginx開啟gzip后不會計算資源的大小,直接用chunked模式。

2.nginx關(guān)閉chunked_transfer_encoding

(1) 在reqeust header里不使用gzip,也就是不加accept-encoding:gzip

test.so文件大小 結(jié)果
100B 能正常返回content-length,沒有transfer-encoding頭
69M 能正常返回content-length,沒有transfer-encoding頭
3072M 能正常返回content-length,沒有transfer-encoding頭

因為能很容易的知道文件大小,所以nginx還是能返回content-length。

(2)在request header里加入gzip,accepting-encoding:gzip

test.so文件大小 結(jié)果
100B 沒有content-length和transfer-encoding頭,不論客戶端connection為keep-alive還是close,服務(wù)端返回的connection頭都是close
69M 沒有content-length和transfer-encoding頭,不論客戶端connection為keep-alive還是close,服務(wù)端返回的connection頭都是close
3072M 沒有content-length和transfer-encoding頭,不論客戶端connection為keep-alive還是close,服務(wù)端返回的connection頭都是close

這就是上面說的第三種情況,不知道大小,也不支持trunked,那就必須使用短連接來標(biāo)示結(jié)束。

問題解決方案

咨詢了中間件組的同事,以前也遇到類似的問題,因為升級了Weblogic導(dǎo)致客戶端解析XML出錯,因為使用了chunked模式,中間有一些格式化的字符,而客戶端解析的代碼并沒有考慮chunked模式的解析,導(dǎo)致解析出錯。

因為我們客戶端必須用content-length展示進度,因此不能用chunked模式,Weblogic可以把chunked模式關(guān)閉。用下面的方法:

#!java weblogic.WLST 
connect('username’,'password', 't3://localhost:7001')
edit()
startEdit()
cd("Servers/AdminServer/WebServer/AdminServer")
cmo.setChunkedTransferDisabled(true)
save()
activate()
exit()

改了之后,確實不返回chunked了,但是也沒有content-length,因為Weblogic就是不提前獲取文件大小,而是強制加了connection:close,也就是前邊說的第三種,通過連接結(jié)束標(biāo)識數(shù)據(jù)結(jié)束。最后只能把這些資源放倒apache里了。

到此,相信大家對“Http協(xié)議Content-Length實例分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI