HTTP的長連接和短連接

轉載至:https://www.cnblogs.com/cswuyg/p/3653263.html

 

 本文總結&分享網絡編程中涉及的長連接、短連接概念。

    關鍵字: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規定一個用戶最多兩個連接的指導意義:流水線技術實現好了,那麼多連接並不能提升性能。我也覺得如此,併發已經在單個連接中實現了,多連接就沒啥必要,除非瓶頸在於單個連接上的資源限制迫使不得不多開連接搶資源。

    目前瀏覽器並不太重視這個技術,畢竟性能提升有限。

 

七、學習資料

1、HTTP Keep-Alive模式:http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html

2、瀏覽器的併發請求限制:http://www.zhihu.com/question/20474326

3、RFC文檔 connection部分:http://tools.ietf.org/html/rfc2616#page-44

4、C/C++網絡編程中的TCP保活: http://blog.csdn.net/weiwangchao_/article/details/7225338

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權威指南》第四章 連接管理 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章