本文總結&分享網絡編程中觸及的長鏈接、短鏈接概念。
關鍵字: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?!禜TTP威望指南》說到,有有些陳舊的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?;睿?nbsp;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威望指南》第四章 鏈接管理