热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Okhttp对http2的支持简单分析

在《Okhttp之RealConnection建立链接简单分析》一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打

在《 Okhttp之RealConnection建立链接简单分析》一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开tcp链接之后就调用establishProtocol,本篇主要是对此方法来进行分析:

   */
private void establishProtocol(ConnectionSpecSelector connectionSpecSelector) throws IOException {
//不是https请求即普通的http请求,那么协议定义为http/1.1
if (route.address().sslSocketFactory() == null) {
protocol = Protocol.HTTP_1_1;
socket = rawSocket;
return;
}
//https请求
//使用SSLSocket,同时会赋值protocol和socket,主要用来判断服务器是否支持http2.0
connectTls(connectionSpecSelector);

//对http2的支持
if (protocol == Protocol.HTTP_2) {
socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
http2COnnection= new Http2Connection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.listener(this)
.build();
http2Connection.start();
}
}

大体上看该方法主要做了如下工作:
1、如果是http请求的话,就将协议protocol定义为http/1.1
2、如果是https的请求的话,就调用connectTls创建一个SSLSocket,然后通过一些列的方法调用(比如三次握手啦之类的),初始化protocol:关键代码如下:

   sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, address.url().host(), address.url().port(), true /* autoClose */);

// Configure the socket's ciphers, TLS versions, and extensions.
ConnectionSpec cOnnectionSpec= connectionSpecSelector.configureSecureSocket(sslSocket);
if (connectionSpec.supportsTlsExtensions()) {
Platform.get().configureTlsExtensions(
sslSocket, address.url().host(), address.protocols());
}

// Force handshake. This can throw!
sslSocket.startHandshake();
Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession());
//////////////分割线//////////////////////
String maybeProtocol = connectionSpec.supportsTlsExtensions()
? Platform.get().getSelectedProtocol(sslSocket)
: null;
//socket为sslSocket
socket = sslSocket;
//将输入输出流交给Okio
source = Okio.buffer(Okio.source(socket));
sink = Okio.buffer(Okio.sink(socket));

protocol = maybeProtocol != null
? Protocol.get(maybeProtocol)
: Protocol.HTTP_1_1;

为什么是https的话就开始判断是否是http2呢?其实是因为http2就目前来说在实际使用中,只用于https协议场景下,通过握手阶段extension字段协商而来,当然HTTP2.0其实可以支持非HTTPS的(参考资料),但是现在主流的浏览器像chrome,firefox表示还是只支持基于 TLS 部署的HTTP2.0协议,所以要想升级成HTTP2.0还是先升级HTTPS为好(参考资料)。而上面的代码分割线以上的部门就是做了这这些协商工作。

分割线一下的部分就是对协议的初始化了。其实,如果打印Okhttp的response对象的toString(),会发现其协议版本也在里面体现出来:Response{protocol=http/1.1, code=200, message=OK, url=http://www.xxx.com/}

3、最后就进入了对http2的处理。
总的来说establishProtocol方法的作用查询服务器对http版本的支持情况,如果是http2协议的服务器,就交给http2Connection 来处理请求。

当协商完成后客户端和服务端就可以发送所谓的链接序言了,且该序列后跟着一个设置帧(SETTING),其可为空帧,什么是链接序言呢?用字符串表示的话就是:字符串PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)
那么okhttp是怎么发送序言的呢?追踪下Http2Connection的start方法最终进入:

  void start(boolean sendConnectionPreface) throws IOException {
if (sendConnectionPreface) {
//发送链接序言
writer.connectionPreface();
//发送setting帧
writer.settings(okHttpSettings);
//省略部分代码
}
//开启线程执行readerRunnable
new Thread(readerRunnable).start(); // Not a daemon thread.
}

上面的代码connectionPreface其实就是发送链接序言的:

 static final ByteString CONNECTION_PREFACE
= ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");

public synchronized void connectionPreface() throws IOException {
//发送链接序言
sink.write(CONNECTION_PREFACE.toByteArray());
sink.flush();
}

HTTP/2中基本的协议单位是帧。每个帧都有不同的类型和用途。 例如,报头(HEADERS)和数据(DATA)帧组成了基本的HTTP 请求和响应;其他帧例如 设置(SETTINGS)、窗口更新(WINDOW_UPDATE)和推送承诺(PUSH_PROMISE) 是用来实现HTTP/2的其他功能(摘自《http/2.0中文翻译》。
而Okhttp2对也是严格按照这些帧的种类来进行数据的读取和解析:具体在readerRunnable的execute方法:

     protected void execute() {
//省略了部分代码,比如try cath
reader.readConnectionPreface(this);
//不断读取http2.0的数据帧
while (reader.nextFrame(false, this)) {
}

}
}

其核心逻辑也就是在Http2Reader的nextFrame中:

 public boolean nextFrame(boolean requireSettings, Handler handler) throws IOException {
try {
source.require(9); // Frame header size
} catch (IOException e) {
return false; // This might be a normal socket close.
}
//省略了部分代码

switch (type) {
case TYPE_DATA:
readData(handler, length, flags, streamId);
break;

case TYPE_HEADERS:
readHeaders(handler, length, flags, streamId);
break;

case TYPE_PRIORITY:
readPriority(handler, length, flags, streamId);
break;

case TYPE_RST_STREAM:
readRstStream(handler, length, flags, streamId);
break;

case TYPE_SETTINGS:
readSettings(handler, length, flags, streamId);
break;

case TYPE_PUSH_PROMISE:
readPushPromise(handler, length, flags, streamId);
break;

case TYPE_PING:
readPing(handler, length, flags, streamId);
break;

case TYPE_GOAWAY:
readGoAway(handler, length, flags, streamId);
break;

case TYPE_WINDOW_UPDATE:
readWindowUpdate(handler, length, flags, streamId);
break;

default:
// Implementations MUST discard frames that have unknown or unsupported types.
source.skip(length);
}
return true;
}

到此为止,本篇博文简单解析完毕,至于对每一帧怎么处理以及每个帧的含义可以参考《Http2.0中文解析》。在这里就不做大篇幅的说明了(因为某些具体的处理细节我还没高透彻)


推荐阅读
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 现在比较流行使用静态网站生成器来搭建网站,博客产品着陆页微信转发页面等。但每次都需要对服务器进行配置,也是一个重复但繁琐的工作。使用DockerWeb,只需5分钟就能搭建一个基于D ... [详细]
  • centos6.8 下nginx1.10 安装 ... [详细]
  • UMTS基础知识汇总
    协议框架23G接口UMTS实体EntityNameDescriptionAuCAuthenticationCenterCBCCellBroadcastCenterC-RNCCon ... [详细]
  • Bro是一款强大的网络安全工具,以及协议识别与统计的工具。Broisapowerfulnetworkanalysisframeworkthatismuchdifferentfro ... [详细]
  • 近来公司前后端星散,前端自力供应页面和静态效劳很天然的就想到了用nginx去做静态效劳器。同时因为跨域了,就想应用nginx的反向代办去处置惩罚一下跨域,但是在解决题目的同时,发明 ... [详细]
author-avatar
宝宝田小丫
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有