OKHttp原码分析(六)之RealConnection

一,概述

okhttp是底层实现框架,与httpURLconnection是同一级别的。OKHttp底层建立网络连接的关键就是RealConnection类。RealConnection类底层封装socket,是真正的创建连接者。分析这个类之后就明白了OKHttp与httpURLconnection的本质不同点。

RealConnection是底层类,都是间接的被其他类调用,所以到目前为止还未发现使用这个类的地方。不要感觉到源码分析断层了,因为这个类太重要了,所以不得不拿出来单独分析。先记住这个类,等下一篇blog分析httpStream时就会将RealConnection与其他部分连接起来。

二,RealConnection对象的创建。

RealConnection 对象在StreamAllocation 类中得到。

有人说我是怎么知道RealConnection对象是在StreamAllocation 类中得到,因为我已经把源码看过一遍了,所以找到了这个地方。看这篇blog的小伙伴只需先记着,下一篇blog就会有说明。

得到RealConnection 对象的代码是在newStream方法中,核心代码是:

RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);

findHealthyConnection方法又调用了findConnection方法。findConnection方法的源码是:

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    synchronized (connectionPool) {
    ;//创建RealConnection对象。
    RealConnection newConnection = new RealConnection(selectedRoute);
    acquire(newConnection);
    ;//连接网络
    newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
        connectionRetryEnabled);
    routeDatabase().connected(newConnection.route());

    return newConnection;
  }

这段代码主要做了两件事情,一是创建RealConnection 对象,二是建立网络连接。

下面看建立网络连接的代码。

三,建立网络连接(重点)

建立网络连接使用的代码是RealConnection 类的connect方法。
connect方法又调用了buildConnection方法。
buildConnection方法的源码是:

private void buildConnection(int connectTimeout, int readTimeout, int writeTimeout,
      ConnectionSpecSelector connectionSpecSelector) throws IOException {
    connectSocket(connectTimeout, readTimeout);//连接socket
    establishProtocol(readTimeout, writeTimeout, connectionSpecSelector);//建立协议
  }

首先看connectSocket方法:

 private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    ;//得到socket对象
    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket()
        : new Socket(proxy);

    rawSocket.setSoTimeout(readTimeout);
    try {
    ;//连接socket
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
    } catch (ConnectException e) {
      throw new ConnectException("Failed to connect to " + route.socketAddress());
    }
    source = Okio.buffer(Okio.source(rawSocket));//从socket中获取source 对象。
    sink = Okio.buffer(Okio.sink(rawSocket));//从socket中获取sink 对象。
  }

这个方法里做了三件事情:
1,创建socket对象。
2,连接socket。
3,使用okio包从socket中得到source 对象和sink 对象。

下面看下Okio.source(rawSocket)的原码:

  public static Source source(Socket socket) throws IOException {
    if (socket == null) throw new IllegalArgumentException("socket == null");
    AsyncTimeout timeout = timeout(socket);
    Source source = source(socket.getInputStream(), timeout);//从socket中得到输入流对象。
    return timeout.source(source);
  }

看到这儿大家会有种熟悉的感觉,这不就是我们熟悉的socket编码吗?对,这就是面向socket编程。
所以OKHttp本质是封装的socket。

四,建立协议

建立协议的方法是establishProtocol。方法原码如下:

private void establishProtocol(int readTimeout, int writeTimeout,
      ConnectionSpecSelector connectionSpecSelector) throws IOException {
    if (route.address().sslSocketFactory() != null) {
      connectTls(readTimeout, writeTimeout, connectionSpecSelector);
    } else {
      protocol = Protocol.HTTP_1_1;
      socket = rawSocket;
    }

    if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) {
      socket.setSoTimeout(0); // Framed connection timeouts are set per-stream.

      FramedConnection framedConnection = new FramedConnection.Builder(true)
          .socket(socket, route.address().url().host(), source, sink)
          .protocol(protocol)
          .listener(this)
          .build();
      framedConnection.start();

      // Only assign the framed connection once the preface has been sent successfully.
      this.allocationLimit = framedConnection.maxConcurrentStreams();
      this.framedConnection = framedConnection;
    } else {
      this.allocationLimit = 1;
    }
  }

从代码中可以看到,使用哪一种协议主要看route.address().sslSocketFactory() != null的返回值是true还是false。

RealConnection 的route对象来自于streamAllocation对象,streamAllocation对象在RetryAndFollowUpInterceptor类的intercept方法中被创建。创建streamAllocation对象的核心代码是:

streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url()));

下面看createAddress方法的源码:

  private Address createAddress(HttpUrl url) {
    SSLSocketFactory sslSocketFactory = null;
    HostnameVerifier hostnameVerifier = null;
    CertificatePinner certificatePinner = null;
    if (url.isHttps()) {
      sslSocketFactory = client.sslSocketFactory();
      hostnameVerifier = client.hostnameVerifier();
      certificatePinner = client.certificatePinner();
    }

    return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
        sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
        client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());

这儿出现了SSLSocketFactory 对象,如果url是https则sslSocketFactory有值。如果url是http则sslSocketFactory等于null.所以如果url是https则使用http2.0或spdy协议。如果url是http则使用http1.1协议。

五,SSLSocket的使用

当使用https时,route.address().sslSocketFactory() != null返回值是true,则会调用connectTls方法。
connectTls方法源码是:

 private void connectTls(int readTimeout, int writeTimeout,
      ConnectionSpecSelector connectionSpecSelector) throws IOException {
    Address address = route.address();
    SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
    boolean success = false;
    SSLSocket sslSocket = null;
    try {
      // 创建SSLSocket对象。
      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());

      // Verify that the socket's certificates are acceptable for the target host.
      if (!address.hostnameVerifier().verify(address.url().host(), sslSocket.getSession())) {
        X509Certificate cert = (X509Certificate) unverifiedHandshake.peerCertificates().get(0);
        throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified:"
            + "\n    certificate: " + CertificatePinner.pin(cert)
            + "\n    DN: " + cert.getSubjectDN().getName()
            + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
      }

      // Check that the certificate pinner is satisfied by the certificates presented.
      address.certificatePinner().check(address.url().host(),
          unverifiedHandshake.peerCertificates());

      // Success! Save the handshake and the ALPN protocol.
      String maybeProtocol = connectionSpec.supportsTlsExtensions()
          ? Platform.get().getSelectedProtocol(sslSocket)
          : null;
      socket = sslSocket;//给socket字段重新赋值。
      source = Okio.buffer(Okio.source(socket));
      sink = Okio.buffer(Okio.sink(socket));
      handshake = unverifiedHandshake;
      protocol = maybeProtocol != null
          ? Protocol.get(maybeProtocol)
          : Protocol.HTTP_1_1;
      success = true;
    } catch (AssertionError e) {
      if (Util.isAndroidGetsocknameError(e)) throw new IOException(e);
      throw e;
    } finally {
      if (sslSocket != null) {
        Platform.get().afterHandshake(sslSocket);
      }
      if (!success) {
        closeQuietly(sslSocket);
      }
    }
  }

ssl的作用是对网络请求进行加密,让请求更安全。

猜你喜欢

转载自blog.csdn.net/fightingxia/article/details/71310512
今日推荐