本文总结&分享网络编程中触及的长链接、短链接概念。
关键字: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威望指南》第四章 链接管理