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