?? ?本文總結(jié)&分享網(wǎng)絡(luò)編程中涉及的長連接、短連接概念。
?? ?關(guān)鍵字:Keep-Alive,并發(fā)連接數(shù)限制,TCP,HTTP
???? HTTP1.1規(guī)定了默認(rèn)保持長連接(HTTP persistent connection
,也有翻譯為持久連接),數(shù)據(jù)傳輸完成了保持TCP連接不斷開(不發(fā)RST包、不四次握手),等待在同域名下繼續(xù)用這個(gè)通道傳輸數(shù)據(jù);相反的就是短連接。
HTTP首部的Connection:
Keep-alive是HTTP1.0瀏覽器和服務(wù)器的實(shí)驗(yàn)性擴(kuò)展,當(dāng)前的HTTP1.1
RFC2616文檔沒有對(duì)它做說明,因?yàn)樗枰墓δ芤呀?jīng)默認(rèn)開啟,無須帶著它,但是實(shí)踐中可以發(fā)現(xiàn),瀏覽器的報(bào)文請(qǐng)求都會(huì)帶上它。如果HTTP1.1版本的HTTP請(qǐng)求報(bào)文不希望使用長連接,則要在HTTP請(qǐng)求報(bào)文首部加上Connection:
close。《HTTP權(quán)威指南》提到,有部分古老的HTTP1.0
代理不理解Keep-alive,而導(dǎo)致長連接失效:客戶端-->代理-->服務(wù)端,客戶端帶有Keep-alive,而代理不認(rèn)識(shí),于是將報(bào)文原封不動(dòng)轉(zhuǎn)給了服務(wù)端,服務(wù)端響應(yīng)了Keep-alive,也被代理轉(zhuǎn)發(fā)給了客戶端,于是保持了“客戶端-->代理”連接和“代理-->服務(wù)端”連接不關(guān)閉,但是,當(dāng)客戶端第發(fā)送第二次請(qǐng)求時(shí),代理會(huì)認(rèn)為當(dāng)前連接不會(huì)有請(qǐng)求了,于是忽略了它,長連接失效。書上也介紹了解決方案:當(dāng)發(fā)現(xiàn)HTTP版本為1.0時(shí),就忽略Keep-alive,客戶端就知道當(dāng)前不該使用長連接。其實(shí),在實(shí)際使用中不需要考慮這么多,很多時(shí)候代理是我們自己控制的,如Nginx代理,代理服務(wù)器有長連接處理邏輯,服務(wù)端無需做patch處理,常見的是客戶端跟Nginx代理服務(wù)器使用HTTP1.1協(xié)議&長連接,而Nginx代理服務(wù)器跟后端服務(wù)器使用HTTP1.0協(xié)議&短連接。
??? 在實(shí)際使用中,HTTP頭部有了Keep-Alive這個(gè)值并不代表一定會(huì)使用長連接,客戶端和服務(wù)器端都可以無視這個(gè)值,也就是不按標(biāo)準(zhǔn)來,譬如我自己寫的HTTP客戶端多線程去下載文件,就可以不遵循這個(gè)標(biāo)準(zhǔn),并發(fā)的或者連續(xù)的多次GET請(qǐng)求,都分開在多個(gè)TCP通道中,每一條TCP通道,只有一次GET,GET完之后,立即有TCP關(guān)閉的四次握手,這樣寫代碼更簡單,這時(shí)候雖然HTTP頭有Connection:
Keep-alive,但不能說是長連接。正常情況下客戶端瀏覽器、web服務(wù)端都有實(shí)現(xiàn)這個(gè)標(biāo)準(zhǔn),因?yàn)樗鼈兊奈募中∮侄啵3珠L連接減少重新開TCP連接的開銷很有價(jià)值。
???? 以前使用libcurl做的上傳/下載,就是短連接,抓包可以看到:1、每一條TCP通道只有一個(gè)POST;2、在數(shù)據(jù)傳輸完畢可以看到四次握手包。只要不調(diào)用curl_easy_cleanup,curl的handle就可能一直有效,可復(fù)用。這里說可能,因?yàn)檫B接是雙方的,如果服務(wù)器那邊關(guān)掉了,那么我客戶端這邊保留著也不能實(shí)現(xiàn)長連接。???
??? 如果是使用windows的WinHTTP庫,則在POST/GET數(shù)據(jù)的時(shí)候,雖然我關(guān)閉了句柄,但這時(shí)候TCP連接并不會(huì)立即關(guān)閉,而是等一小會(huì)兒,這時(shí)候是WinHTTP庫底層支持了跟Keep-alive所需要的功能:即便沒有Keep-alive,WinHTTP庫也可能會(huì)加上這種TCP通道復(fù)用的功能,而其它的網(wǎng)絡(luò)庫像libcurl則不會(huì)這么做。以前觀察過WinHTTP庫不會(huì)及時(shí)斷開TCP連接。
??? 客戶端的長連接不可能無限期的拿著,會(huì)有一個(gè)超時(shí)時(shí)間,服務(wù)器有時(shí)候會(huì)告訴客戶端超時(shí)時(shí)間,譬如:
???? 上圖中的Keep-Alive:
timeout=20,表示這個(gè)TCP通道可以保持20秒。另外還可能有max=XXX,表示這個(gè)長連接最多接收XXX次請(qǐng)求就斷開。對(duì)于客戶端來說,如果服務(wù)器沒有告訴客戶端超時(shí)時(shí)間也沒關(guān)系,服務(wù)端可能主動(dòng)發(fā)起四次握手?jǐn)嚅_TCP連接,客戶端能夠知道該TCP連接已經(jīng)無效;另外TCP還有心跳包來檢測當(dāng)前連接是否還活著,方法很多,避免浪費(fèi)資源。
三、長連接的數(shù)據(jù)傳輸完成識(shí)別
??? 使用長連接之后,客戶端、服務(wù)端怎么知道本次傳輸結(jié)束呢?兩部分:1是判斷傳輸數(shù)據(jù)是否達(dá)到了Content-Length指示的大?。?動(dòng)態(tài)生成的文件沒有Content-Length,它是分塊傳輸(chunked),這時(shí)候就要根據(jù)chunked編碼來判斷,chunked編碼的數(shù)據(jù)在最后有一個(gè)空chunked塊,表明本次傳輸數(shù)據(jù)結(jié)束。更細(xì)節(jié)的介紹可以看這篇文章。
四、并發(fā)連接數(shù)的數(shù)量限制
??? 在web開發(fā)中需要關(guān)注瀏覽器并發(fā)連接的數(shù)量,RFC文檔說,客戶端與服務(wù)器最多就連上兩通道,但服務(wù)器、個(gè)人客戶端要不要這么做就隨人意了,有些服務(wù)器就限制同時(shí)只能有1個(gè)TCP連接,導(dǎo)致客戶端的多線程下載(客戶端跟服務(wù)器連上多條TCP通道同時(shí)拉取數(shù)據(jù))發(fā)揮不了威力,有些服務(wù)器則沒有限制。瀏覽器客戶端就比較規(guī)矩,知乎這里有分析,限制了同域名下能啟動(dòng)若干個(gè)并發(fā)的TCP連接去下載資源。并發(fā)數(shù)量的限制也跟長連接有關(guān)聯(lián),打開一個(gè)網(wǎng)頁,很多個(gè)資源的下載可能就只被放到了少數(shù)的幾條TCP連接里,這就是TCP通道復(fù)用(長連接)。如果并發(fā)連接數(shù)少,意味著網(wǎng)頁上所有資源下載完需要更長的時(shí)間(用戶感覺頁面打開卡了);并發(fā)數(shù)多了,服務(wù)器可能會(huì)產(chǎn)生更高的資源消耗峰值。瀏覽器只對(duì)同域名下的并發(fā)連接做了限制,也就意味著,web開發(fā)者可以把資源放到不同域名下,同時(shí)也把這些資源放到不同的機(jī)器上,這樣就完美解決了。
五、容易混淆的概念——TCP的keep
alive和HTTP的Keep-alive
?? ?TCP的keep
alive是檢查當(dāng)前TCP連接是否活著;HTTP的Keep-alive是要讓一個(gè)TCP連接活久點(diǎn)。它們是不同層次的概念。
??? TCP keep alive的表現(xiàn):
??? 當(dāng)一個(gè)連接“一段時(shí)間”沒有數(shù)據(jù)通訊時(shí),一方會(huì)發(fā)出一個(gè)心跳包(Keep
Alive包),如果對(duì)方有回包則表明當(dāng)前連接有效,繼續(xù)監(jiān)控。
這個(gè)“一段時(shí)間”可以設(shè)置。
WinHttp庫的設(shè)置:
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的設(shè)置:
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是空閑多久發(fā)送一個(gè)心跳包,CURLOPT_TCP_KEEPINTVL是心跳包間隔多久發(fā)一個(gè)。
打開網(wǎng)頁抓包,發(fā)送心跳包和關(guān)閉連接如下:
?
??? 從上圖可以看到,大概過了44秒,客戶端發(fā)出了心跳包,服務(wù)器及時(shí)回應(yīng),本TCP連接繼續(xù)保持。到了空閑60秒的時(shí)候,服務(wù)器主動(dòng)發(fā)起FIN包,斷開連接。
六、HTTP 流水線技術(shù)
??? 使用了HTTP長連接(HTTP persistent connection
)之后的好處,包括可以使用HTTP 流水線技術(shù)(HTTP
pipelining,也有翻譯為管道化連接),它是指,在一個(gè)TCP連接內(nèi),多個(gè)HTTP請(qǐng)求可以并行,下一個(gè)HTTP請(qǐng)求在上一個(gè)HTTP請(qǐng)求的應(yīng)答完成之前就發(fā)起。從wiki上了解到這個(gè)技術(shù)目前并沒有廣泛使用,使用這個(gè)技術(shù)必須要求客戶端和服務(wù)器端都能支持,目前有部分瀏覽器完全支持,而服務(wù)端的支持僅需要:按HTTP請(qǐng)求順序正確返回Response(也就是請(qǐng)求&響應(yīng)采用FIFO模式),wiki里也特地指出,只要服務(wù)器能夠正確處理使用HTTP
pipelinning的客戶端請(qǐng)求,那么服務(wù)器就算是支持了HTTP pipelining。
??? 由于要求服務(wù)端返回響應(yīng)數(shù)據(jù)的順序必須跟客戶端請(qǐng)求時(shí)的順序一致,這樣也就是要求FIFO,這容易導(dǎo)致Head-of-line
blocking:第一個(gè)請(qǐng)求的響應(yīng)發(fā)送影響到了后邊的請(qǐng)求,因?yàn)檫@個(gè)原因?qū)е翲TTP流水線技術(shù)對(duì)性能的提升并不明顯(wiki提到,這個(gè)問題會(huì)在HTTP2.0中解決)。另外,使用這個(gè)技術(shù)的還必須是冪等的HTTP方法,因?yàn)榭蛻舳藷o法得知當(dāng)前已經(jīng)處理到什么地步,重試后可能發(fā)生不可預(yù)測的結(jié)果。POST方法不是冪等的:同樣的報(bào)文,第一次POST跟第二次POST在服務(wù)端的表現(xiàn)可能會(huì)不一樣。
??? 在HTTP長連接的wiki中提到了HTTP1.1的流水線技術(shù)對(duì)RFC規(guī)定一個(gè)用戶最多兩個(gè)連接的指導(dǎo)意義:流水線技術(shù)實(shí)現(xiàn)好了,那么多連接并不能提升性能。我也覺得如此,并發(fā)已經(jīng)在單個(gè)連接中實(shí)現(xiàn)了,多連接就沒啥必要,除非瓶頸在于單個(gè)連接上的資源限制迫使不得不多開連接搶資源。
??? 目前瀏覽器并不太重視這個(gè)技術(shù),畢竟性能提升有限。
本文所在: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權(quán)威指南》第四章 連接管理
2014.7.27補(bǔ)充:閱讀完《HTTP權(quán)威指南》第四章,補(bǔ)充長連接的理論知識(shí)。