OKHttp原码分析(六)之RealConnection

来源:互联网 发布:c语言入门经典txt下载 编辑:程序博客网 时间:2024/05/17 22:14

一,概述

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的作用是对网络请求进行加密,让请求更安全。

1 0
原创粉丝点击