客户端用TCP/IP从服务器上获取数据时,需要一个连通客户端和服务器的连接,连接通过“三次握手”建立,通过“四次握手”释放。如果每次获取数据都创建一个独占的连接,并在数据传输完毕后释放,这种连接就叫作“短连接”。而一个能够供多个请求多次传输数据,并在数据传输后不会立即释放的连接称为“长连接”,也称为持久连接。
使用长连接,TCP连接会在第一个HTTP请求连接创建时建立,并在最后一个HTTP请求关闭后的一段时间内结束,所有元素的传输过程都在这个 TCP 连接上完成,即HTTP设备在事务处理结束后仍将TCP 连接保持在打开状态,以便为未来的HTTP请求重用现存连接。这样不仅优化了网页的加载速度,也降低了服务器的压力,服务器不必再为客户端的多次请求向系统频繁申请、释放资源。
此外,长连接是推送服务的技术核心,推送服务已经是各大 APP 的“标配”功能,PUSH功能的实现,正是基于长连接的全双工通信能力。当客户端与服务器建立“长连接”后,服务器就能随时随地地“找”到客户端并PUSH数据。
短连接会频繁进行socket的创建,但在并发量大,而每个用户又不需频繁操作的情况下需要短连接很有用,如果用长连接,而且同时用成千上万的用户,每个用户都占有一个连接的话,可想而知服务器的压力有多大。
持久连接
HTTP1.1允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现在的连接。如一个web页面的大部分内嵌图片都来自同一个web站点,而且一部分指向其它对象的超链接通常指向同一个站点,因此初始化了对某服务器HTTP请求的应用程序可能在将来对那台服务器发起更多的请求,重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段,而且已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速低进行数据传输。
下图描述了在串行连接上实现的4个HTTP事务的时间线与在一条持久连接上实现同样事务所需的时间线

持久连接在HTTP/1.0称为keep-alive连接,在HTTP/1.1中称为persistent连接。长连接是在请求头中添加指定的请求字段实现的,它们的格式都是
Connection: keep-alive
http 1.0中默认是关闭长连接的,需要在http头加入"Connection: keep-alive",才能启用Keep-Alive;http 1.1中默认启用keep-alive,需要在http头中加入"Connection: close ",才关闭。目前大部分浏览器都是用http1.1协议,也就是说默认都会发起keep-alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。
如果服务器支持且愿意为下一条请求重用此连接,就会在响应中包含相同的首部。若没有,服务器就会在发回响应报文后关闭连接。客户端就是通过检测响应中是否包含Connection: keep-alive响应首部来判断服务器是否会在发送响应后关闭连接的。如果响应中没有该首部,客户端就认为服务器不支持keep-alive,会在响应报文接收后关闭连接。

在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,避免了建立或者重新建立连接的次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的TCP连接意味着更少的系统内核调用,socket的accept()和close()调用)。具体来说,好处有
1.由于同时打开的连接的减少,会使用较少的CPU和内存。
2.允许请求和应答的HTTP 管线化(将多个HTTP request 整批提交的技术,而在传送过程中不需要先等待服务器的回应)。
3.降低网络阻塞(TCP连接减少)
4.不需要进行握手,减少了后续请求的延迟。
但长时间的TCP连接容易导致系统资源无效占用,管理持久连接不当会累积大量的空闲连接,耗费本地及远程客户端和服务器的资源。有时比重复利用连接带来的损失还更大。这就需要有一种调节手段,控制持久连接的有效期及最大允许请求数量。
持久连接的控制
Keep-alive的行为可以用响应头中的keep-alive通用首部中指定的、由逗号分隔的选项来调节。格式如
Keep-Alive:max=5,timeout=120
这个头是可选的,只有在包含了Connection: keep-alive首部的情况下才可使用它。参数timeout估计了服务器希望将链接保持在活跃状态的时间,参数max是在keep-alive响应首部发送的,它估计了服务器还希望为多少个事务保持此连接的活跃状态。上述选项的意思是服务器最多还会为另外5个事务保持连接在打开状态,或者将打开状态保持到连接空闲了2两分钟后关闭。
httpd守护进程一般都提供了keep-alive timeout时间设置参数,比如nginx的keepalive_timeout和Apache的keepalivetimeout。这个 keepalive_timeout时间值意味着:一个http产生的TCP连接在传送完最后一个响应后,还需要保持keepalive_timeout 时间后才开始关闭这个连接。
[root@test24266 conf]# cat nginx.conf | grepkeepalive
keepalive_timeout 120;
如果客户端不想在一条persistent连接上发送更多请求了,就应该在最后一条请求中包含Connection: Close首部。只要服务器决定在事务处理结束后关闭连接,就必须在响应中包含Connection: Close首部。但不发送Connection: Close首部也并不意味着服务器承诺永远将连接保持在打开状态。
所有的HTTP客户端、服务器都可以在任意时刻关闭一条TCP传输连接,通常会在一条报文结束时关闭连接。但是服务器都无法确定在它关闭空闲连接的那一刻,在客户端有没有数据要发送,如果出现这种情况,客户端就会在写入半截请求报文时发现出现了连接错误。
每条HTTP响应都有精确的content-length首部,用以描述响应主体的尺寸,如果缺省该首部或包含了错误的长度指示,就需要依赖服务器发出的连接关闭来说明数据的真实末尾。如果客户端收到一条随连接关闭而结束的HTTP响应,且实际传输的长度与content-length并不匹配,接收端就应该置疑长度的正确性。
HTTP应用程序要正确做好处理非预期关闭的准备,如果客户端执行事务的过程中,传输连接关闭了,那么除非事务处理会带来一些副作用,否则客户端就应该重新打开连接,并重试一次。如果一个事务不管执行一次还是多次,得到的结果都是相同的,这个事务就是幂等的(GET\HEAD\PUT\DELETE\TRACE\OPTIONS)。要发送一条非幂等请求(post),就需要等待来自前一条请求的响应状态。
Tcp连接是双向的,每一端都有一个输入队列和一个输出队列,用于数据的读和写。放入一端输出队列中的数据最终会出现在另一端的输入队列中。
应用程序可以关闭TCP输入和输出信道中的任一个,或将两者都关闭。

关闭连接的输出信道总是安全的,连接另一端的对等实体会在从其缓冲区中读出所有数据之后收到一条通知,说明流结束了,这样就知道连接被关闭了。
关闭连接的输入信道是比较危险的,除非知道另一端不打算继续发送数据了。如果另一端向你已关闭的输入信道发送数据,操作系统就会向另一端的机器回送一条TCP“连接被对端重置”的报文。这种情况下大部分操作系统会删除对端还未读取的所有缓存数据。
因此,实现正常关闭的应用程序首先应该关闭它们的输出信道,等待连接另一端的对等实体关闭它的输出信道,当两端都告诉对方它们不会再有数据交互时,连接就可以完全关闭,而不会有重置危险。但困难的是,无法确保对等实体会实现半关闭,或对其进行检查,因此想要正常关闭连接的应用程序应该先半关闭其输出信道,然后周期地检查其输入信道的状态(查找数据或流末尾)。如果在一定的时间区间内对端没有关闭输入信道,应用程序可以强制关闭连接,以节省资源。
网友评论