HTTP keep-alive學(xué)習(xí)

公司某產(chǎn)品的網(wǎng)頁有安全保護(hù)機(jī)制,每過一段時間就會自動登出,沒辦法畢竟是對安全要求比較高的產(chǎn)品,所以只能不停地登錄。但是惱火的是我發(fā)現(xiàn)每次第一次登錄一定會報密碼錯誤,這怎么可能???后來程序猿小哥告訴我這是因為和數(shù)據(jù)庫的連接已經(jīng)斷掉了,第一次登錄只是起到了刷新的作用。emmm。。。。
然后又有人告訴我,這個問題可以通過heart beat來解決,比如系統(tǒng)如果10分鐘沒有任何流量就斷開這個鏈接,可以選擇每5分鐘發(fā)一個心跳包,然后有一點(diǎn)流量,系統(tǒng)就不會斷開這個鏈接了。heart beat是實現(xiàn)keep-alive的一種形式,在自學(xué)過程中我順便也學(xué)了一下keep-alive的東西。
轉(zhuǎn)載一篇我覺得寫得比較清楚的講keep-alive的博客。

————————————————————————
故事發(fā)生在10月份的一次面試經(jīng)歷中,本來我不想說出來丟人顯眼,但是為了警醒自己和告誡后人,我決定寫成博文發(fā)出來。因為在面試過程中,我講在2009年寫過QQ農(nóng)場助手,在這期間深入學(xué)習(xí)了HTTP協(xié)議,而且在2010-05-18寫了博文:HTTP協(xié)議及其POST與GET操作差異 & C#中如何使用POST、GET等。面試官說既然我熟悉HTTP協(xié)議,就問“當(dāng)HTTP采用keepalive模式,當(dāng)客戶端向服務(wù)器發(fā)生請求之后,客戶端如何判斷服務(wù)器的數(shù)據(jù)已經(jīng)發(fā)生完成?”

說實話,當(dāng)時我懵了,一直沒有關(guān)注過keepalive模式。我只知道:HTTP協(xié)議中客戶端發(fā)送一個小請求,服務(wù)器響應(yīng)以所期望的信息(例如一個html文件或一副gif圖像)。服務(wù)器通常在發(fā)送回所請求的數(shù)據(jù)之后就關(guān)閉連接。這樣客戶端讀數(shù)據(jù)時會返回EOF(-1),就知道數(shù)據(jù)已經(jīng)接收完全了。我就這樣被面試官判了死刑?。?!說我完全停留在表面,沒有深入(當(dāng)時真的很受打擊,一直自認(rèn)為技術(shù)還不錯?。?。我當(dāng)時真的很想找各種借口:

  • 之前沒有用到HTTP的keepalive模式,所以沒有深入

  • 好久沒有用HTTP協(xié)議,細(xì)節(jié)忘了

  • 實習(xí)的東西跟HTTP協(xié)議沒有關(guān)系,用得少了就忘了

  • 。。。。。。

覺得各種解釋都是那么蒼白無力!我再次感嘆書到用時方恨少,也感嘆一個人的時間是多么的有限(曾一度想成為一個IT專業(yè)全才),根本沒有精力面面俱到,而且當(dāng)沒有真正使用一個東西的時候,往往會忽略掉很多細(xì)節(jié)。朋友如果你也答不上來,請認(rèn)真細(xì)看下文,不要懷著浮躁了的心,說不定下次就有人問你這個問題。

1、什么是Keep-Alive模式?

我們知道HTTP協(xié)議采用“請求-應(yīng)答”模式,當(dāng)使用普通模式,即非KeepAlive模式時,每個請求/應(yīng)答客戶和服務(wù)器都要新建一個連接,完成之后立即斷開連接(HTTP協(xié)議為無連接的協(xié)議);當(dāng)使用Keep-Alive模式(又稱持久連接、連接重用)時,Keep-Alive功能使客戶端到服務(wù)器端的連接持續(xù)有效,當(dāng)出現(xiàn)對服務(wù)器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。

[圖片上傳中...(image-d8a696-1532937277099-0)]

http 1.0中默認(rèn)是關(guān)閉的,需要在http頭加入"Connection: Keep-Alive",才能啟用Keep-Alive;http 1.1中默認(rèn)啟用Keep-Alive,如果加入"Connection: close ",才關(guān)閉。目前大部分瀏覽器都是用http1.1協(xié)議,也就是說默認(rèn)都會發(fā)起Keep-Alive的連接請求了,所以是否能完成一個完整的Keep-Alive連接就看服務(wù)器設(shè)置情況。

2、啟用Keep-Alive的優(yōu)點(diǎn)

從上面的分析來看,啟用Keep-Alive模式肯定更高效,性能更高。因為避免了建立/釋放連接的開銷。下面是RFC 2616上的總結(jié):

    1. By opening and closing fewer TCP connections, CPU time is saved in routers and hosts (clients, servers, proxies, gateways, tunnels, or caches), and memory used for TCP protocol control blocks can be saved in hosts.

    2. HTTP requests and responses can be pipelined on a connection. Pipelining allows a client to make multiple requests without waiting for each response, allowing a single TCP connection to be used much more efficiently, with much lower elapsed time.

    3. Network congestion is reduced by reducing the number of packets caused by TCP opens, and by allowing TCP sufficient time to determine the congestion state of the network.

    4. Latency on subsequent requests is reduced since there is no time spent in TCP's connection opening handshake.

    5. HTTP can evolve more gracefully, since errors can be reported without the penalty of closing the TCP connection. Clients using future versions of HTTP might optimistically try a new feature, but if communicating with an older server, retry with old semantics after an error is reported.

RFC 2616(P47)還指出:單用戶客戶端與任何服務(wù)器或代理之間的連接數(shù)不應(yīng)該超過2個。一個代理與其它服務(wù)器或代碼之間應(yīng)該使用超過2 * N的活躍并發(fā)連接。這是為了提高HTTP響應(yīng)時間,避免擁塞(冗余的連接并不能代碼執(zhí)行性能的提升)。

3、回到我們的問題(即如何判斷消息內(nèi)容/長度的大?。浚?/h1>

Keep-Alive模式,客戶端如何判斷請求所得到的響應(yīng)數(shù)據(jù)已經(jīng)接收完成(或者說如何知道服務(wù)器已經(jīng)發(fā)生完了數(shù)據(jù))?我們已經(jīng)知道了,Keep-Alive模式發(fā)送玩數(shù)據(jù)HTTP服務(wù)器不會自動斷開連接,所有不能再使用返回EOF(-1)來判斷(當(dāng)然你一定要這樣使用也沒有辦法,可以想象那效率是何等的低)!下面我介紹兩種來判斷方法。

3.1、使用消息首部字段Conent-Length

故名思意,Conent-Length表示實體內(nèi)容長度,客戶端(服務(wù)器)可以根據(jù)這個值來判斷數(shù)據(jù)是否接收完成。但是如果消息中沒有Conent-Length,那該如何來判斷呢?又在什么情況下會沒有Conent-Length呢?請繼續(xù)往下看……

3.2、使用消息首部字段Transfer-Encoding

當(dāng)客戶端向服務(wù)器請求一個靜態(tài)頁面或者一張圖片時,服務(wù)器可以很清楚的知道內(nèi)容大小,然后通過Content-length消息首部字段告訴客戶端需要接收多少數(shù)據(jù)。但是如果是動態(tài)頁面等時,服務(wù)器是不可能預(yù)先知道內(nèi)容大小,這時就可以使用Transfer-Encoding:chunk模式來傳輸數(shù)據(jù)了。即如果要一邊產(chǎn)生數(shù)據(jù),一邊發(fā)給客戶端,服務(wù)器就需要使用"Transfer-Encoding: chunked"這樣的方式來代替Content-Length。

chunk編碼將數(shù)據(jù)分成一塊一塊的發(fā)生。Chunked編碼將使用若干個Chunk串連而成,由一個標(biāo)明長度為0的chunk標(biāo)示結(jié)束。每個Chunk分為頭部和正文兩部分,頭部內(nèi)容指定正文的字符總數(shù)(十六進(jìn)制的數(shù)字)和數(shù)量單位(一般不寫),正文部分就是指定長度的實際內(nèi)容,兩部分之間用回車換行(CRLF)隔開。在最后一個長度為0的Chunk中的內(nèi)容是稱為footer的內(nèi)容,是一些附加的Header信息(通??梢灾苯雍雎裕?/p>

Chunk編碼的格式如下:

Chunked-Body = *chunk
"0" CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF

hex-no-zero = <HEX excluding "0">

chunk-size = hex-no-zero *HEX
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)

footer = *entity-header

即Chunk編碼由四部分組成:1、0至多個chunk塊,2、"0" CRLF,3、footer,4、CRLF****.而每個chunk塊由:chunk-size、chunk-ext(可選)、CRLF、chunk-data、CRLF組成。

4、消息長度的總結(jié)

其實,上面2中方法都可以歸納為是如何判斷http消息的大小、消息的數(shù)量。RFC 2616對消息的長度總結(jié)如下:一個消息的transfer-length(傳輸長度)是指消息中的message-body(消息體)的長度。當(dāng)應(yīng)用了transfer-coding(傳輸編碼),每個消息中的message-body(消息體)的長度(transfer-length)由以下幾種情況決定(優(yōu)先級由高到低):

  • 任何不含有消息體的消息(如1XXX、204、304等響應(yīng)消息和任何頭(HEAD,首部)請求的響應(yīng)消息),總是由一個空行(CLRF)結(jié)束。
  • 如果出現(xiàn)了Transfer-Encoding頭字段 并且值為非“identity”,那么transfer-length由“chunked” 傳輸編碼定義,除非消息由于關(guān)閉連接而終止。
  • 如果出現(xiàn)了Content-Length頭字段,它的值表示entity-length(實體長度)和transfer-length(傳輸長度)。如果這兩個長度的大小不一樣(i.e.設(shè)置了Transfer-Encoding頭字段),那么將不能發(fā)送Content-Length頭字段。并且如果同時收到了Transfer-Encoding字段和Content-Length頭字段,那么必須忽略Content-Length字段。
  • 如果消息使用媒體類型“multipart/byteranges”,并且transfer-length 沒有另外指定,那么這種自定界(self-delimiting)媒體類型定義transfer-length 。除非發(fā)送者知道接收者能夠解析該類型,否則不能使用該類型。
  • 由服務(wù)器關(guān)閉連接確定消息長度。(注意:關(guān)閉連接不能用于確定請求消息的結(jié)束,因為服務(wù)器不能再發(fā)響應(yīng)消息給客戶端了。)

為了兼容HTTP/1.0應(yīng)用程序,HTTP/1.1的請求消息體中必須包含一個合法的Content-Length頭字段,除非知道服務(wù)器兼容HTTP/1.1。一個請求包含消息體,并且Content-Length字段沒有給定,如果不能判斷消息的長度,服務(wù)器應(yīng)該用用400 (bad request) 來響應(yīng);或者服務(wù)器堅持希望收到一個合法的Content-Length字段,用 411 (length required)來響應(yīng)。

所有HTTP/1.1的接收者應(yīng)用程序必須接受“chunked” transfer-coding (傳輸編碼),因此當(dāng)不能事先知道消息的長度,允許使用這種機(jī)制來傳輸消息。消息不應(yīng)該夠同時包含 Content-Length頭字段和non-identity transfer-coding。如果一個消息同時包含non-identity transfer-coding和Content-Length ,必須忽略Content-Length 。

5、HTTP頭字段總結(jié)

最后我總結(jié)下HTTP協(xié)議的頭部字段。

  • 1、 Accept:告訴WEB服務(wù)器自己接受什么介質(zhì)類型,/ 表示任何類型,type/* 表示該類型下的所有子類型,type/sub-type。
  • 2、 Accept-Charset: 瀏覽器申明自己接收的字符集
    Accept-Encoding: 瀏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支持壓縮,支持什么壓縮方法(gzip,deflate)
    Accept-Language:瀏覽器申明自己接收的語言
    語言跟字符集的區(qū)別:中文是語言,中文有多種字符集,比如big5,gb2312,gbk等等。
  • 3、 Accept-Ranges:WEB服務(wù)器表明自己是否接受獲取其某個實體的一部分(比如文件的一部分)的請求。bytes:表示接受,none:表示不接受。
  • 4、 Age:當(dāng)代理服務(wù)器用自己緩存的實體去響應(yīng)請求時,用該頭部表明該實體從產(chǎn)生到現(xiàn)在經(jīng)過多長時間了。
  • 5、 Authorization:當(dāng)客戶端接收到來自WEB服務(wù)器的 WWW-Authenticate 響應(yīng)時,用該頭部來回應(yīng)自己的身份驗證信息給WEB服務(wù)器。
  • 6、 Cache-Control:請求:no-cache(不要緩存的實體,要求現(xiàn)在從WEB服務(wù)器去?。?br> max-age:(只接受 Age 值小于 max-age 值,并且沒有過期的對象)
    max-stale:(可以接受過去的對象,但是過期時間必須小于 max-stale 值)
    min-fresh:(接受其新鮮生命期大于其當(dāng)前 Age 跟 min-fresh 值之和的緩存對象)
    響應(yīng):public(可以用 Cached 內(nèi)容回應(yīng)任何用戶)
    private(只能用緩存內(nèi)容回應(yīng)先前請求該內(nèi)容的那個用戶)
    no-cache(可以緩存,但是只有在跟WEB服務(wù)器驗證了其有效后,才能返回給客戶端)
    max-age:(本響應(yīng)包含的對象的過期時間)
    ALL: no-store(不允許緩存)
  • 7、 Connection:請求:close(告訴WEB服務(wù)器或者代理服務(wù)器,在完成本次請求的響應(yīng)后,斷開連接,不要等待本次連接的后續(xù)請求了)。
    keepalive(告訴WEB服務(wù)器或者代理服務(wù)器,在完成本次請求的響應(yīng)后,保持連接,等待本次連接的后續(xù)請求)。
    響應(yīng):close(連接已經(jīng)關(guān)閉)。
    keepalive(連接保持著,在等待本次連接的后續(xù)請求)。
    Keep-Alive:如果瀏覽器請求保持連接,則該頭部表明希望 WEB 服務(wù)器保持連接多長時間(秒)。例如:Keep-Alive:300
  • 8、 Content-Encoding:WEB服務(wù)器表明自己使用了什么壓縮方法(gzip,deflate)壓縮響應(yīng)中的對象。例如:Content-Encoding:gzip
  • 9、Content-Language:WEB 服務(wù)器告訴瀏覽器自己響應(yīng)的對象的語言。
  • 10、Content-Length: WEB 服務(wù)器告訴瀏覽器自己響應(yīng)的對象的長度。例如:Content-Length: 26012
  • 11、Content-Range: WEB 服務(wù)器表明該響應(yīng)包含的部分對象為整個對象的哪個部分。例如:Content-Range: bytes 21010-47021/47022
  • 12、Content-Type: WEB 服務(wù)器告訴瀏覽器自己響應(yīng)的對象的類型。例如:Content-Type:application/xml
  • 13、ETag:就是一個對象(比如URL)的標(biāo)志值,就一個對象而言,比如一個 html 文件,如果被修改了,其 Etag 也會別修改,所以ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 服務(wù)器判斷一個對象是否改變了。比如前一次請求某個 html 文件時,獲得了其 ETag,當(dāng)這次又請求這個文件時,瀏覽器就會把先前獲得的 ETag 值發(fā)送給WEB 服務(wù)器,然后 WEB 服務(wù)器會把這個 ETag 跟該文件的當(dāng)前 ETag 進(jìn)行對比,然后就知道這個文件有沒有改變了。
  • 14、 Expired:WEB服務(wù)器表明該實體將在什么時候過期,對于過期了的對象,只有在跟WEB服務(wù)器驗證了其有效性后,才能用來響應(yīng)客戶請求。是 HTTP/1.0 的頭部。例如:Expires:Sat, 23 May 2009 10:02:12 GMT
  • 15、 Host:客戶端指定自己想訪問的WEB服務(wù)器的域名/IP 地址和端口號。例如:Host:rss.sina.com.cn
  • 16、 If-Match:如果對象的 ETag 沒有改變,其實也就意味著對象沒有改變,才執(zhí)行請求的動作。
  • 17、 If-None-Match:如果對象的 ETag 改變了,其實也就意味著對象也改變了,才執(zhí)行請求的動作。
  • 18、 If-Modified-Since:如果請求的對象在該頭部指定的時間之后修改了,才執(zhí)行請求的動作(比如返回對象),否則返回代碼304,告訴瀏覽器該對象沒有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
  • 19、 If-Unmodified-Since:如果請求的對象在該頭部指定的時間之后沒修改過,才執(zhí)行請求的動作(比如返回對象)。
  • 20、 If-Range:瀏覽器告訴 WEB 服務(wù)器,如果我請求的對象沒有改變,就把我缺少的部分給我,如果對象改變了,就把整個對象給我。瀏覽器通過發(fā)送請求對象的 ETag 或者 自己所知道的最后修改時間給 WEB 服務(wù)器,讓其判斷對象是否改變了??偸歉?Range 頭部一起使用。
  • 21、 Last-Modified:WEB 服務(wù)器認(rèn)為對象的最后修改時間,比如文件的最后修改時間,動態(tài)頁面的最后產(chǎn)生時間等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT
  • 22、 Location:WEB 服務(wù)器告訴瀏覽器,試圖訪問的對象已經(jīng)被移到別的位置了,到該頭部指定的位置去取。例如:Location:http://i0.sinaimg.cn/dy/deco/2008/0528/sinahome_0803_ws_005_text_0.gif
  • 23、 Pramga:主要使用 Pramga: no-cache,相當(dāng)于 Cache-Control: no-cache。例如:Pragma:no-cache
  • 24、 Proxy-Authenticate: 代理服務(wù)器響應(yīng)瀏覽器,要求其提供代理身份驗證信息。Proxy-Authorization:瀏覽器響應(yīng)代理服務(wù)器的身份驗證請求,提供自己的身份信息。
  • 25、 Range:瀏覽器(比如 Flashget 多線程下載時)告訴 WEB 服務(wù)器自己想取對象的哪部分。例如:Range: bytes=1173546-
  • 26、 Referer:瀏覽器向 WEB 服務(wù)器表明自己是從哪個 網(wǎng)頁/URL 獲得/點(diǎn)擊 當(dāng)前請求中的網(wǎng)址/URL。例如:Referer:http://www.sina.com/
  • 27、 Server: WEB 服務(wù)器表明自己是什么軟件及版本等信息。例如:Server:Apache/2.0.61 (Unix)
  • 28、 User-Agent: 瀏覽器表明自己的身份(是哪種瀏覽器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2、0、0、14
  • 29、 Transfer-Encoding: WEB 服務(wù)器表明自己對本響應(yīng)消息體(不是消息體里面的對象)作了怎樣的編碼,比如是否分塊(chunked)。例如:Transfer-Encoding: chunked
  • 30、 Vary: WEB服務(wù)器用該頭部的內(nèi)容告訴 Cache 服務(wù)器,在什么條件下才能用本響應(yīng)所返回的對象響應(yīng)后續(xù)的請求。假如源WEB服務(wù)器在接到第一個請求消息時,其響應(yīng)消息的頭部為:Content-Encoding: gzip; Vary: Content-Encoding那么 Cache 服務(wù)器會分析后續(xù)請求消息的頭部,檢查其 Accept-Encoding,是否跟先前響應(yīng)的 Vary 頭部值一致,即是否使用相同的內(nèi)容編碼方法,這樣就可以防止 Cache 服務(wù)器用自己 Cache 里面壓縮后的實體響應(yīng)給不具備解壓能力的瀏覽器。例如:Vary:Accept-Encoding
  • 31、 Via: 列出從客戶端到 OCS 或者相反方向的響應(yīng)經(jīng)過了哪些代理服務(wù)器,他們用什么協(xié)議(和版本)發(fā)送的請求。當(dāng)客戶端請求到達(dá)第一個代理服務(wù)器時,該服務(wù)器會在自己發(fā)出的請求里面添加 Via 頭部,并填上自己的相關(guān)信息,當(dāng)下一個代理服務(wù)器收到第一個代理服務(wù)器的請求時,會在自己發(fā)出的請求里面復(fù)制前一個代理服務(wù)器的請求的Via 頭部,并把自己的相關(guān)信息加到后面,以此類推,當(dāng) OCS 收到最后一個代理服務(wù)器的請求時,檢查 Via 頭部,就知道該請求所經(jīng)過的路由。例如:Via:1.0 236.D0707195.sina.com.cn:80 (squid/2.6.STABLE13)

===============================================================================
HTTP 請求消息頭部實例:
Host:rss.sina.com.cn
User-Agent:Mozilla/5、0 (Windows; U; Windows NT 5、1; zh-CN; rv:1、8、1、14) Gecko/20080404 Firefox/2、0、0、14
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0、9,text/plain;q=0、8,image/png,/;q=0、5
Accept-Language:zh-cn,zh;q=0、5
Accept-Encoding:gzip,deflate
Accept-Charset:gb2312,utf-8;q=0、7,*;q=0、7
Keep-Alive:300
Connection:keep-alive
Cookie:userId=C5bYpXrimdmsiQmsBPnE1Vn8ZQmdWSm3WRlEB3vRwTnRtW <-- Cookie
If-Modified-Since:Sun, 01 Jun 2008 12:05:30 GMT
Cache-Control:max-age=0
HTTP 響應(yīng)消息頭部實例:
Status:OK - 200 <-- 響應(yīng)狀態(tài)碼,表示 web 服務(wù)器處理的結(jié)果。
Date:Sun, 01 Jun 2008 12:35:47 GMT
Server:Apache/2、0、61 (Unix)
Last-Modified:Sun, 01 Jun 2008 12:35:30 GMT
Accept-Ranges:bytes
Content-Length:18616
Cache-Control:max-age=120
Expires:Sun, 01 Jun 2008 12:37:47 GMT
Content-Type:application/xml
Age:2
X-Cache:HIT from 236-41、D07071951、sina、com、cn <-- 反向代理服務(wù)器使用的 HTTP 頭部
Via:1.0 236-41.D07071951.sina.com.cn:80 (squid/2.6.STABLE13)
Connection:close

本節(jié)摘自:http://ynhu33.blog.51cto.com/412835/408801

——最后我想說:“怪自己學(xué)藝不精,浪費(fèi)了一次機(jī)會(而且是我最想進(jìn)的公司)”

希望老天再給我一次機(jī)會。

PS:還有一點(diǎn)加速了我的死亡,我學(xué)習(xí)過Android開發(fā)

但是用的是JAVA,經(jīng)理說研究Android開發(fā)就得用NDK,那才是核心。

作者:吳秦
出處:http://www.cnblogs.com/skynet/
本文基于署名 2.5 中國大陸許可協(xié)議發(fā)布,歡迎轉(zhuǎn)載,演繹或用于商業(yè)目的,但是必須保留本文的署名吳秦(包含鏈接).

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容