彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
网络通信系列文章序
彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
前面简单说了下HttpRequestRetryHandler,这篇主要分析下ConnectionReuseStrategy,ConnectionKeepAliveStrategy,连接的重用和长连接
1:基础介绍
1.1)Keep-Alive解析
http协议作为上层应用层协议,其是基于TCP/IP协议,UDP协议传输层上进行的;http协议通过socket这个套接字完成客户端和服务端的通信;
http协议目前主要有两个版本HTTP 1.0和HTTP 1.1,他们都是无状态协议
在HTTP 1.0中,每一次请求响应之后,下一次的请求需要断开之前的连接,再重新开始;
在HTTP 1.1中,使用keep-alive在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。
因此便出现了Connection: keep-alive的设置,用于建立长连接,即我们所说的Keep-Alive模式;
如上图,左图是HTTP 1.0 ; 右图是HTTP 1.1
http 1.0中默认是关闭的,需要在http头加入”Connection: Keep-Alive”,才能启用Keep-Alive;
在1.0中,如果客户端浏览器支持Keep-Alive,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有Connection: Keep-Alive的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive。这样一来,客户端和服务器之间的HTTP连接就会被保持,不会断开(超过Keep-Alive规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接
http 1.1中默认启用Keep-Alive,如果加入”Connection: close “,才关闭。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间;
一次完成的http请求是否能够保持,同时也要靠服务端是否具备Keep-Alive能力;
1.2)如何判断客户端已经完整的接收到服务端的数据,针对HTTP 1.0和HTTP 1.1
在java中,使用socket编程的时候,我们经常看到如下代码
Socket socket = new Socket("localhost",10086);
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine()) != -1){
}
1
2
3
4
5
6
7
所以在普通的socket编程中,我们可以使用EOF(-1)来判断是否完整的接收到服务端的返回的数据;这是因为在一次普通的http请求中,即没有添加Connection: Keep-Alive属性的http请求中,服务端响应之后,会断开连接,顾使用EOF判断是准确的;
但是这种方式针对添加Connection: Keep-Alive属性的http请求来说,就无法生效了;
我们可以通过头消息中的Conent-Length字段来判断;但是对于动态页面或者zip格式的内容,服务端一般会采用Transfer-Encoding: chunked”这样的方式来代替Content-Length;
因此
在Http 1.0及之前版本中,content-length字段可有可无。
在http1.1及之后版本。如果是keep alive,则content-length和chunk必然是二选一。若是非keep alive,则和http1.0一样。content-length可有可无。
2:在DefaultRequestDirector.execute方法中有如下代码
// The connection is in or can be brought to a re-usable state.
reuse = reuseStrategy.keepAlive(response, context);
if(reuse) {
// Set the idle duration of this connection
long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
}
1
2
3
4
5
6
7
从这段代码可以看出,当完成一次http请求之后,并没有立即关闭这个tcp连接和释放资源;而是通过可重用策略来判断这个连接能否被保持,以及保持多长时间;
我们先分析下如何判断这个连接是否是可重用的,我们可以通过上面的keepAlive方法来进行具体分析,reuseStrategy的默认实现者为DefaultConnectionReuseStrategy,我们看一下keepAlive方法
//该方法的作用就是在一次请求之后,这个连接能够被保持
//如果返回false,则调用者应该立即关闭连接
//如果返回true,则调用者应该保持这个连接从而可以应用于其他请求
public boolean keepAlive(final HttpResponse response,
final HttpContext context) {
if (response == null) {
throw new IllegalArgumentException
("HTTP response may not be null.");
}
if (context == null) {
throw new IllegalArgumentException
("HTTP context may not be null.");
}
HttpConnection conn = (HttpConnection)
context.getAttribute(ExecutionContext.HTTP_CONNECTION);
//当一个连接没有建立的时候,当然是不可重用的,返回false
if (conn != null && !conn.isOpen())
return false;
// do NOT check for stale connection, that is an expensive operation
// Check for a self-terminating entity. If the end of the entity will
// be indicated by closing the connection, there is no keep-alive.
HttpEntity entity = response.getEntity();
ProtocolVersion ver = response.getStatusLine().getProtocolVersion();
//当返回的entity的长度小于0,并且http协议版本号小于1.0,返回false
if (entity != null) {
if (entity.getContentLength() < 0) {
if (!entity.isChunked() ||
ver.lessEquals(HttpVersion.HTTP_1_0)) {
// if the content length is not known and is not chunk
// encoded, the connection cannot be reused
return false;
}
}
}
// Check for the "Connection" header. If that is absent, check for
// the "Proxy-Connection" header. The latter is an unspecified and
// broken but unfortunately common extension of HTTP.
//HTTP.CONN_DIRECTIVE的值为Connection,即获取响应头信息中的Connection字段的内容
HeaderIterator hit = response.headerIterator(HTTP.CONN_DIRECTIVE);
//如果没有这个头信息,则寻找Proxy-Connection的头信息
if (!hit.hasNext()){
//Proxy-Connection是http1.0 时代的产物。老旧的代理,如果设置 connection: keepalive,
//代理原样转发给服务器,服务器会以为要建立长久连接,但是代理并不支持,这样就出问题了。
//所以改为设置 proxy-connection: keepalive,如果是新的代理,
//支持 keepalive,它会认得这个头,并改成 connection: keepalive 转发给服务器,
//顺利建立持久连接;如果是老的代理,它不认识,会原样转发,这时候服务器也不会建立持久连接
hit = response.headerIterator("Proxy-Connection");
}
if (hit.hasNext()) {
try {
TokenIterator ti = createTokenIterator(hit);
boolean keepalive = false;
while (ti.hasNext()) {
final String token = ti.nextToken();
//如果Connection:close则返回false
if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
return false;
}//如果Connection:Keep-Alive则返回true
else if (HTTP.CONN_KEEP_ALIVE.equalsIgnoreCase(token)) {
// continue the loop, there may be a "close" afterwards
keepalive = true;
}
}
if (keepalive)
return true;
// neither "close" nor "keep-alive", use default policy
} catch (ParseException px) {
// invalid connection header means no persistent connection
// we don't have logging in HttpCore, so the exception is lost
return false;
}
}
// default since HTTP/1.1 is persistent, before it was non-persistent
return !ver.lessEquals(HttpVersion.HTTP_1_0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
总结
1:该方法的作用就是在一次请求之后,这个连接能否被保持,如果返回false,则调用者应该立即关闭连接;如果返回true,则调用者应该保持这个连接从而可以应用于其他请求
2:由于HTTP 1.0时代,还不能有效支持connection,顾多了一个Proxy-Connection头信息,该字段的出现的原因是: 在HTTP 1.0中老旧的代理,如果设置 connection: keepalive,代理原样转发给服务器,服务器会以为要建立长久连接,但是代理并不支持,这样就出问题了。所以改为设置 proxy-connection: keepalive,如果是新的代理,支持 keepalive,它会认得这个头,并改成 connection: keepalive 转发给服务器,顺利建立持久连接;如果是老的代理,它不认识,会原样转发,这时候服务器也不会建立持久连接
当一个请求视为可重用之后,即keepAlive返回true,那这么链接能一直保持链接状态?会不会超过一定时间,连接就断开?
答案是会的,当链接超过预设时间,会自动断开;
当一个链接是可重用的,我们就可以通过如下代码获得这个链接可以存活的时间
keepAliveStrategy.getKeepAliveDuration
1
keepAliveStrategy的实现为DefaultConnectionKeepAliveStrategy
public class DefaultConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
HeaderElementIterator it = new BasicHeaderElementIterator(
//HTTP.CONN_KEEP_ALIVE的为“Keep-Alive”
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch(NumberFormatException ignore) {
}
}
}
return -1;
}
}
代码很简单,就是通过Keep-Alive头信息中,获得timeout的值,作为超时时间;单位毫秒;
如请求头中 Keep-Alive: timeout=5, max=100
---------------------
作者:yi_master
来源:CSDN
原文:https://blog.csdn.net/yi_master/article/details/80595372
版权声明:本文为博主原创文章,转载请附上博文链接!
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析的更多相关文章
- c#网络通信框架networkcomms内核解析 序言
NetworkComms网络通信框架序言 networkcomms是我遇到的写的最优美的代码,很喜欢,推荐给大家:) 基于networkcomms2.3.1开源版本( gplv3)协议,写了一些文章, ...
- c#网络通信框架networkcomms内核解析之十 支持优先级的自定义线程池
NetworkComms网络通信框架序言 本例基于networkcomms2.3.1开源版本 gplv3协议 如果networkcomms是一顶皇冠,那么CommsThreadPool(自定义线程池 ...
- c#网络通信框架networkcomms内核解析之八 数据包的核心处理器
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制 ...
- django开发_七牛云CNAME解析
CNAME 简介 CNAME 即指别名记录,也被称为规范名字.这种记录允你将多个名字映射到同一台计算机. 当需要将域名指向另一个域名,再由另一个域名提供 ip地址,就需要添加 CNAME 记录. 为什 ...
- c#网络通信框架networkcomms内核解析之一 消息传送
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之一 消息传送2
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之三 消息同步调用
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 客户端发送消息给服务器,服务器计算结果返回 ...
- c#网络通信框架networkcomms内核解析之九 自定义处理方法的运行机制
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们自己写的处理方法都称之为自定义处理方法 比如,我们在服务器上写的与登陆相关的处理方法 ...
- 《Play for Java》学习笔记(七)数据类型解析——Body parser
一.什么是body parser? body parser(不知道具体如何翻译,~~~~(>_<)~~~~ )指一个HTTP请求 (如POST和PUT操作)所包含的文本内容(body),这 ...
随机推荐
- 第二节:EF Core的常规“增删改”及状态的变化
一. 整体说明 1. 本节用到的表 2. 状态说明补充 ①.Detached: 游离的状态,与数据库没有什么交涉,比如新new一个实体,状态就是Detached. ②.Added: 增加的状态. ③. ...
- 部署elasticsearch(三节点)集群+filebeat+kibana
用途 ▷ 通过各个beat实时收集日志.传输至elasticsearch集群 ▷ 通过kibana展示日志 实验架构 名称:IP地址:CPU:内存 kibana&cerebro:192.168 ...
- Linux学习笔记之Linux磁盘及文件系统管理笔记
Linux磁盘及文件系统管理 CPU,memory(RAM),I/O i/o: disks,ehtercard disks:持久存储数据 接口类型: IDE(ata): 并口,133MB/s;并行总线 ...
- 入门-windows下安装ETH挖矿
对刚入门的区块链开发者来说,刚开始可以在windows本地搭建私有链,便于操作,毕竟,要想真正挖到币还是有难度的,下面以ETH为例,在windows环境下安装并实现挖矿. 步骤一.安装geth环境.下 ...
- session中删除数组中的某一个值 - 购物车例子 - jsp
这篇随笔简单的讲一下在session中移除数组中的某一项内容,比如这里有一个购物车其中有两件商品,需要移除其中洗发水这一件商品. 其实在这个session对象中存储了一个数组,在订购页面时选择商品加入 ...
- CKEditor 4 上传图片
参考资料:Basic Configuration 直接Ctrl+v(粘贴图片)报错信息:上传文件时发生网络错误(networkError:Network error occurred during f ...
- mongodb数据库怎么迁移备份?
1.先进入到mongodb目录的bin目录下,Linux windos是一样 image.png 2.集合备份和恢复 使用mongo自带命令来迁移数据,思路是先导出集合数据再导入到数据库中 导出命 ...
- 人脸跟踪开源项目HyperFT代码算法解析及改进
一.简介 人脸识别已经成为计算机视觉领域中最热门的应用之一,其中,人脸信息处理的第一个环节便是人脸检测和人脸跟踪.人脸检测是指在输入的图像中确定所有人脸的位置.大小和姿势的过程.人脸跟踪是指在图像序列 ...
- Java自学-数字与字符串 比较字符串
Java 比较字符串 示例 1 : 是否是同一个对象 str1和str2的内容一定是一样的! 但是,并不是同一个字符串对象 package character; public class TestSt ...
- AttributeError: module 'matplotlib' has no attribute 'verbose'
AttributeError: module 'matplotlib' has no attribute 'verbose' 翻译:attributeError:模块“matplotlib”没有“ve ...