IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> okhttp之连接 -> 正文阅读

[网络协议]okhttp之连接

okhttp之拦截器中,我们知道建立连接是在ConnectInterceptor中完成的,今天具体分析一下实现细节
在默认情况下,是没有现成的连接使用的,所以从新建开始看

一、建立连接

    val newConnection = RealConnection(connectionPool, route)
    newConnection.connect(
        connectTimeout,
        readTimeout,
        writeTimeout,
        pingIntervalMillis,
        connectionRetryEnabled,
        call,
        eventListener
    )

新建了RealConnection对象,调用connect方法

fun connect() {
    while (true) {
      try {
        // 1 HTTPS
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
        } else {
          // 2 HTTP
          connectSocket(connectTimeout, readTimeout, call, eventListener)
        }
        establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
        break
      }
    }
  }

两种情况分开分析

1、HTTPS连接建立

进入connectTunnel

private fun connectTunnel() {
    //构建不带敏感字段的请求,用来打通隧道
    var tunnelRequest: Request = createTunnelRequest()
    val url = tunnelRequest.url
    for (i in 0 until MAX_TUNNEL_ATTEMPTS) {
      connectSocket(connectTimeout, readTimeout, call, eventListener)
      tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url)
          ?: break 
    }
  }

创建建立隧道用的request,然后调用connectSocket:

private fun connectSocket() {
    //创建套接字
    val rawSocket = when (proxy.type()) {
      Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
      else -> Socket(proxy)
    }
    //连接套接字
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
    }
    //创建用来与服务器通信的通道,okio实现
    source = rawSocket.source().buffer()
    sink = rawSocket.sink().buffer()
  }

可以看到是新建了一个套接字,然后调用connect方法。这里开始创建了TCP的连接,接下来就是创建SSL隧道(因为SSL是应用层连接,我们也可以从这里看出来SSL是架设在TCP之上的,而这种分层的协议也很方便添加应用层协议上去。),开始进入createTunnel方法:

private fun createTunnel(): Request? {
    var nextRequest = tunnelRequest
    //建立SSL协议的字段
    val requestLine = "CONNECT ${url.toHostHeader(includeDefaultPort = true)} HTTP/1.1"
    while (true) {
      val source = this.source!!
      val sink = this.sink!!
      val tunnelCodec = Http1ExchangeCodec(null, this, source, sink)
      tunnelCodec.writeRequest(nextRequest.headers, requestLine)
      tunnelCodec.finishRequest()
      val response = tunnelCodec.readResponseHeaders(false)!!
          .request(nextRequest)
          .build()
      tunnelCodec.skipConnectBody(response)
      when (response.code) {
        //成功
        HTTP_OK -> {
          return null
        }
        //需要授权
        HTTP_PROXY_AUTH -> {
          if ("close".equals(response.header("Connection"), ignoreCase = true)) {
            return nextRequest
          }
        }
      }
    }
  }

如果需要认证身份的话,需要重复发请求。返回去看剩下的方法establishProtocol:

private fun establishProtocol() {
    //sslSocketFactory在HTTP协议的时候为空,HTTPS不为空
    if (route.address.sslSocketFactory == null) {
      //如果有h2_prior_knowledge字段,调用启动HTTP2
      if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
        socket = rawSocket
        protocol = Protocol.H2_PRIOR_KNOWLEDGE
        startHttp2(pingIntervalMillis)
        return
      }
      //HTTP1.1
      protocol = Protocol.HTTP_1_1
      return
    }
    //TLS握手
    connectTls(connectionSpecSelector)
    if (protocol === Protocol.HTTP_2) {
      startHttp2(pingIntervalMillis)
    }
  }

假如支持HTTP2,启动HTTP2,否则是HTTP1.1,然后是TLS握手,HTTP2的部分我们不做展开,这里看一下TLS握手:

private fun connectTls(connectionSpecSelector: ConnectionSpecSelector) {
    val address = route.address
    val sslSocketFactory = address.sslSocketFactory
    var success = false
    var sslSocket: SSLSocket? = null
    try {
      //创建SSL套接字
      sslSocket = sslSocketFactory!!.createSocket(
          rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket
      //密码对以及扩展等的配置
      val connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
      if (connectionSpec.supportsTlsExtensions) {
        Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)
      }
      //开始握手
      sslSocket.startHandshake()
      // block for session establishment
      val sslSocketSession = sslSocket.session
      val unverifiedHandshake = sslSocketSession.handshake()
      //验证证书
      if (!address.hostnameVerifier!!.verify(address.url.host, sslSocketSession)) {
        val peerCertificates = unverifiedHandshake.peerCertificates
        if (peerCertificates.isNotEmpty()) {
          throw SSLPeerUnverifiedException()
        } else {
          throw SSLPeerUnverifiedException()
        }
      }
      //握手成功,保存协议
      val maybeProtocol = if (connectionSpec.supportsTlsExtensions) {
        Platform.get().getSelectedProtocol(sslSocket)
      } else {
        null
      }
      socket = sslSocket
      //更新数据通道
      source = sslSocket.source().buffer()
      sink = sslSocket.sink().buffer()
    }
  }

这里实现了TLS的证书验证过程,握手成功后输入和输出通道变成SSL套接字的输入以及输出,也代表我们交流的对象变成了SslSocket,完成了HTTPS的连接建立
然后返回去看一下HTTP的连接建立case

2、HTTP连接建立

调用到了connectSocket方法,这个方法我们已经分析过了,就是socket的连接,实现了TCP的三次握手,只是具体握手细节是系统实现的,我们只需要调用connect就行,最后是establishProtocol,如果没有HTTP2的话,只是对protocal的赋值。

二、总结

除了应用层本身的协议SSL之外,应用层对于连接的建立仅限于对系统接口的调用。
注:由于目前我对HTTP2不甚了解,贸然读源码没有什么实际的意义,所以这里没有做展开

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-17 12:17:20  更:2021-07-17 12:19:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:31:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码