网络通信系列文章序

彻底掌握网络通信(一)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解析的更多相关文章

  1. c#网络通信框架networkcomms内核解析 序言

    NetworkComms网络通信框架序言 networkcomms是我遇到的写的最优美的代码,很喜欢,推荐给大家:) 基于networkcomms2.3.1开源版本( gplv3)协议,写了一些文章, ...

  2. c#网络通信框架networkcomms内核解析之十 支持优先级的自定义线程池

    NetworkComms网络通信框架序言 本例基于networkcomms2.3.1开源版本  gplv3协议 如果networkcomms是一顶皇冠,那么CommsThreadPool(自定义线程池 ...

  3. c#网络通信框架networkcomms内核解析之八 数据包的核心处理器

    NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本  gplv3协议 我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制 ...

  4. django开发_七牛云CNAME解析

    CNAME 简介 CNAME 即指别名记录,也被称为规范名字.这种记录允你将多个名字映射到同一台计算机. 当需要将域名指向另一个域名,再由另一个域名提供 ip地址,就需要添加 CNAME 记录. 为什 ...

  5. c#网络通信框架networkcomms内核解析之一 消息传送

    networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...

  6. c#网络通信框架networkcomms内核解析之一 消息传送2

    networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...

  7. c#网络通信框架networkcomms内核解析之三 消息同步调用

    networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 客户端发送消息给服务器,服务器计算结果返回 ...

  8. c#网络通信框架networkcomms内核解析之九 自定义处理方法的运行机制

    NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本  gplv3协议 我们自己写的处理方法都称之为自定义处理方法 比如,我们在服务器上写的与登陆相关的处理方法 ...

  9. 《Play for Java》学习笔记(七)数据类型解析——Body parser

    一.什么是body parser? body parser(不知道具体如何翻译,~~~~(>_<)~~~~ )指一个HTTP请求 (如POST和PUT操作)所包含的文本内容(body),这 ...

随机推荐

  1. 【Python + Selenium3】自动化测试之DDT数据驱动并生成测试报告以及用yagmail邮件发送文件

    我的文件路径 一.DDT代码: import unittest from time import sleep from selenium import webdriver from ddt impor ...

  2. 迭代子(Iterator)模式

    迭代子模式又叫做游标模式.迭代子模式可以顺序地访问一个聚集中的元素而必暴露聚集的内部表象. 1.  聚集和Java聚集 多个对象在一起形成的总体形成聚集(Aggregate),聚集对象是能够包容一组对 ...

  3. Go在windows下执行命令行指令

    需要在Go写的服务里面调用命令行或者批处理,并根据返回的结果做处理. 在网上搜索了一翻,验证成功,现记录如下: cmd := exec.Command("cmd") // cmd ...

  4. 局域网访问PHP项目网站 用IP地址进入

    先在apache中的 httpd.conf中将 Allow from 127.0.0.1 修改为Allow from all 如果你的是Allow from all的话就不需要改 然后再将 Docum ...

  5. Vue传递方法给页面调用

    很多人在使用vue的时候苦于在vue中写方法,但是在外部甚至在另一个js该如何调用呢? 这个方法就是显示了vue的可以传递方法到页面使得页面任何地方都可以调用 前提得引用文件 这个方法一般多用于加载周 ...

  6. 访问Stack Overflow速度超慢的原因

    很多网站,尤其是国外网站,为了加快网站的速度,都是用了 Google 的 CDN. 但是在天朝,由于某些原因,导致全球最快的 CDN 变成了全球最慢的. 解决方法步骤如下: 下载ReplaceGoog ...

  7. MongoDB Spark Connector 实战指南

    Why Spark with MongoDB? 高性能,官方号称 100x faster,因为可以全内存运行,性能提升肯定是很明显的 简单易用,支持 Java.Python.Scala.SQL 等多种 ...

  8. Webpack 打包工具

    1. webpack 概念 [文档地址](https://www.webpackjs.com/concepts/) 2. webpack 命令使用及相关工具包 1. webpack 安装和打包命令: ...

  9. 记使用pyspider时,任务不执行的问题原因:save太大。

    pyspider使用save传递大量文本时,如果是mysql数据库,有可能出现问题,因为任务表默认用的blob字段.字符数是有限制的. 解决办法就是手动把字段类型改成longblob. 希望作者能直接 ...

  10. 记录一次win2003服务器的IIS服务加载.flv后缀的资源报错404的处理方法

    问题:访问某个域名下的xxxx.flv资源,页面报错404. 解决思路: 1.权限是否给足 user权限给完全控制咯 如果你访问该域名下的其他资源无问题的话就不是介个原因了 2.MIME类型是否少了 ...