1. 程式人生 > >HTTP的長連線和短連線

HTTP的長連線和短連線

  本文總結&分享網路程式設計中涉及的長連線、短連線概念。     關鍵字:Keep-Alive,併發連線數限制,TCP,HTTP

一、什麼是長連線

     HTTP1.1規定了預設保持長連線(HTTP persistent connection ,也有翻譯為持久連線),資料傳輸完成了保持TCP連線不斷開(不發RST包、不四次握手),等待在同域名下繼續用這個通道傳輸資料;相反的就是短連線。

 HTTP首部的Connection: Keep-alive是HTTP1.0瀏覽器和伺服器的實驗性擴充套件,當前的HTTP1.1 RFC2616文件沒有對它做說明,因為它所需要的功能已經預設開啟,無須帶著它,但是實踐中可以發現,瀏覽器的報文請求都會帶上它。如果HTTP1.1版本的HTTP請求報文不希望使用長連線,則要在HTTP請求報文首部加上Connection: close。《HTTP權威指南》提到,有部分古老的HTTP1.0 代理不理解Keep-alive,而導致長連線失效:客戶端-->代理-->服務端,客戶端帶有Keep-alive,而代理不認識,於是將報文原封不動轉給了服務端,服務端響應了Keep-alive,也被代理轉發給了客戶端,於是保持了“客戶端-->代理”連線和“代理-->服務端”連線不關閉,但是,當客戶端第傳送第二次請求時,代理會認為當前連線不會有請求了,於是忽略了它,長連線失效。書上也介紹瞭解決方案:當發現HTTP版本為1.0時,就忽略Keep-alive,客戶端就知道當前不該使用長連線。其實,在實際使用中不需要考慮這麼多,很多時候代理是我們自己控制的,如Nginx代理,代理伺服器有長連線處理邏輯,服務端無需做patch處理,常見的是客戶端跟Nginx代理伺服器使用HTTP1.1協議&長連線,而Nginx代理伺服器跟後端伺服器使用HTTP1.0協議&短連線。

    在實際使用中,HTTP頭部有了Keep-Alive這個值並不代表一定會使用長連線,客戶端和伺服器端都可以無視這個值,也就是不按標準來,譬如我自己寫的HTTP客戶端多執行緒去下載檔案,就可以不遵循這個標準,併發的或者連續的多次GET請求,都分開在多個TCP通道中,每一條TCP通道,只有一次GET,GET完之後,立即有TCP關閉的四次握手,這樣寫程式碼更簡單,這時候雖然HTTP頭有Connection: Keep-alive,但不能說是長連線。正常情況下客戶端瀏覽器、web服務端都有實現這個標準,因為它們的檔案又小又多,保持長連線減少重新開TCP連線的開銷很有價值。

     以前使用
libcurl做的上傳
/下載,就是短連線,抓包可以看到:1、每一條TCP通道只有一個POST;2、在資料傳輸完畢可以看到四次握手包。只要不呼叫curl_easy_cleanup,curl的handle就可能一直有效,可複用。這裡說可能,因為連線是雙方的,如果伺服器那邊關掉了,那麼我客戶端這邊保留著也不能實現長連線。    
    如果是使用windows的WinHTTP庫,則在POST/GET資料的時候,雖然我關閉了控制代碼,但這時候TCP連線並不會立即關閉,而是等一小會兒,這時候是WinHTTP庫底層支援了跟Keep-alive所需要的功能:即便沒有Keep-alive,WinHTTP庫也可能會加上這種TCP通道複用的功能,而其它的網路庫像libcurl則不會這麼做。以前觀察過
WinHTTP庫不會及時斷開TCP連線

二、長連線的過期時間

    客戶端的長連線不可能無限期的拿著,會有一個超時時間,伺服器有時候會告訴客戶端超時時間,譬如:      上圖中的Keep-Alive: timeout=20,表示這個TCP通道可以保持20秒。另外還可能有max=XXX,表示這個長連線最多接收XXX次請求就斷開。對於客戶端來說,如果伺服器沒有告訴客戶端超時時間也沒關係,服務端可能主動發起四次握手斷開TCP連線,客戶端能夠知道該TCP連線已經無效;另外TCP還有心跳包來檢測當前連線是否還活著,方法很多,避免浪費資源。

三、長連線的資料傳輸完成識別

    使用長連線之後,客戶端、服務端怎麼知道本次傳輸結束呢?兩部分:1是判斷傳輸資料是否達到了Content-Length指示的大小;2動態生成的檔案沒有Content-Length,它是分塊傳輸(chunked),這時候就要根據chunked編碼來判斷,chunked編碼的資料在最後有一個空chunked塊,表明本次傳輸資料結束。更細節的介紹可以看這篇文章

四、併發連線數的數量限制

    在web開發中需要關注瀏覽器併發連線的數量,RFC文件說,客戶端與伺服器最多就連上兩通道,但伺服器、個人客戶端要不要這麼做就隨人意了,有些伺服器就限制同時只能有1個TCP連線,導致客戶端的多執行緒下載(客戶端跟伺服器連上多條TCP通道同時拉取資料)發揮不了威力,有些伺服器則沒有限制。瀏覽器客戶端就比較規矩,知乎這裡有分析,限制了同域名下能啟動若干個併發的TCP連線去下載資源。併發數量的限制也跟長連線有關聯,開啟一個網頁,很多個資源的下載可能就只被放到了少數的幾條TCP連線裡,這就是TCP通道複用(長連線)。如果併發連線數少,意味著網頁上所有資源下載完需要更長的時間(使用者感覺頁面開啟卡了);併發數多了,伺服器可能會產生更高的資源消耗峰值。瀏覽器只對同域名下的併發連線做了限制,也就意味著,web開發者可以把資源放到不同域名下,同時也把這些資源放到不同的機器上,這樣就完美解決了。

五、容易混淆的概念——TCP的keep alive和HTTP的Keep-alive

    TCP的keep alive是檢查當前TCP連線是否活著;HTTP的Keep-alive是要讓一個TCP連線活久點。它們是不同層次的概念。     TCP keep alive的表現:     當一個連線“一段時間”沒有資料通訊時,一方會發出一個心跳包(Keep Alive包),如果對方有回包則表明當前連線有效,繼續監控。 這個“一段時間”可以設定。 WinHttp庫的設定

WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL
Sets the interval, in milliseconds, to send a keep-alive packet over the connection. The default interval is 30000 (30 seconds). The minimum interval is 15000 (15 seconds). Using WinHttpSetOption to set a value lower than 15000 will return with ERROR_INVALID_PARAMETER.

libcurl的設定 http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
CURLOPT_TCP_KEEPALIVE Pass a long. If set to 1, TCP keepalive probes will be sent. The delay and frequency of these probes can be controlled by the CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL options, provided the operating system supports them. Set to 0 (default behavior) to disable keepalive probes (Added in 7.25.0). CURLOPT_TCP_KEEPIDLE Pass a long. Sets the delay, in seconds, that the operating system will wait while the connection is idle before sending keepalive probes. Not all operating systems support this option. (Added in 7.25.0) CURLOPT_TCP_KEEPINTVL Pass a long. Sets the interval, in seconds, that the operating system will wait between sending keepalive probes. Not all operating systems support this option. (Added in 7.25.0)
     CURLOPT_TCP_KEEPIDLE是空閒多久傳送一個心跳包,CURLOPT_TCP_KEEPINTVL是心跳包間隔多久發一個。  開啟網頁抓包,傳送心跳包和關閉連線如下:       從上圖可以看到,大概過了44秒,客戶端發出了心跳包,伺服器及時迴應,本TCP連線繼續保持。到了空閒60秒的時候,伺服器主動發起FIN包,斷開連線。

六、HTTP 流水線技術

    使用了HTTP長連線(HTTP persistent connection )之後的好處,包括可以使用HTTP 流水線技術(HTTP pipelining,也有翻譯為管道化連線),它是指,在一個TCP連線內,多個HTTP請求可以並行,下一個HTTP請求在上一個HTTP請求的應答完成之前就發起。從wiki上了解到這個技術目前並沒有廣泛使用,使用這個技術必須要求客戶端和伺服器端都能支援,目前有部分瀏覽器完全支援,而服務端的支援僅需要:按HTTP請求順序正確返回Response(也就是請求&響應採用FIFO模式),wiki裡也特地指出,只要伺服器能夠正確處理使用HTTP pipelinning的客戶端請求,那麼伺服器就算是支援了HTTP pipelining。     由於要求服務端返回響應資料的順序必須跟客戶端請求時的順序一致,這樣也就是要求FIFO,這容易導致Head-of-line blocking:第一個請求的響應傳送影響到了後邊的請求,因為這個原因導致HTTP流水線技術對效能的提升並不明顯(wiki提到,這個問題會在HTTP2.0中解決)。另外,使用這個技術的還必須是冪等的HTTP方法,因為客戶端無法得知當前已經處理到什麼地步,重試後可能發生不可預測的結果。POST方法不是冪等的:同樣的報文,第一次POST跟第二次POST在服務端的表現可能會不一樣。     在HTTP長連線的wiki中提到了HTTP1.1的流水線技術對RFC規定一個使用者最多兩個連線的指導意義:流水線技術實現好了,那麼多連線並不能提升效能。我也覺得如此,併發已經在單個連線中實現了,多連線就沒啥必要,除非瓶頸在於單個連線上的資源限制迫使不得不多開連線搶資源。     目前瀏覽器並不太重視這個技術,畢竟效能提升有限。  本文所在:http://www.cnblogs.com/cswuyg/p/3653263.html

七、學習資料

3、RFC文件 connection部分:http://tools.ietf.org/html/rfc2616#page-44 5、HTTP persistent connection: http://en.wikipedia.org/wiki/HTTP_persistent_connection 6、HTTP pipelining:http://en.wikipedia.org/wiki/HTTP_pipelining 7、Head-of-line blocking:http://en.wikipedia.org/wiki/Head-of-line_blocking 8、《HTTP權威指南》第四章 連線管理  2014.7.27補充:閱讀完《HTTP權威指南》第四章,補充長連線的理論知識。