ConnectInterceptor是okhttp中负责和服务端建立连接的一个拦截器,ConnectInterceptor类中代码看起来是不多,但千万不要被它纯洁的外表给欺骗了,实际上负责的操作都被封装到其他的类里了。下面来看代码:
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//获取StreamAllocation对象,这是在之前的拦截器中就创建好的,这里只是获取一遍
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//获取到HttpCodec对象,这是一个用来封装request和response操作的对象
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//获取到链接
RealConnection connection = streamAllocation.connection();
//交给下个拦截器执行
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
上面的代码就是ConnectInterceptor类中的全部代码了,主要就是获取了几个对象,然后交给组织处理了。其中StreamAllocation是在之前的拦截器中就已经创建好的,这里主要看一下HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);这句代码应该是这个拦截器的核心,通过调用newStream(),获取了一条connection连接。
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
。。。。。
try {
//找到一个健康的连接
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
//用连接生成HttpCodec
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
newStream方法中主要的任务就是找到一条可用的连接,然后生成HttpCodec。
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, int pingIntervalMillis, boolean connectionRetryEnabled,
boolean doExtensiveHealthChecks) throws IOException {
while (true) {
//1、找到一条连接
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
synchronized (connectionPool) {
// 2、判断连接是否是新生成的,如果 successCount==0 表示新生成的,连接可用 直接返回
if (candidate.successCount == 0) {
return candidate;
}
}
// Do a (potentially slow) check to confirm that the pooled connection is still good. If it
// isn't, take it out of the pool and start again.
// 3、连接不是新生成的,则检查连接是否健康
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
//4、如果不健康 禁止创建新的stream,继续下一次循环
noNewStreams();
continue;
}
return candidate;
}
}
findHealthyConnection中执行了一个while循环来获取connection,在循环里一共执行了四步,注释中已经说明。这里面有几个方法需要来看一下,findConnection()、candidate.isHealthy()和noNewStreams()。其中findConnection()放在最后说,先来看另外两个方法。
public boolean isHealthy(boolean doExtensiveChecks) {
if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
return false;
}
if (http2Connection != null) {
return !http2Connection.isShutdown();
}
if (doExtensiveChecks) {
try {
int readTimeout = socket.getSoTimeout();
try {
socket.setSoTimeout(1);
if (source.exhausted()) {
return false; // Stream is exhausted; socket is closed.
}
return true;
} finally {
socket.setSoTimeout(readTimeout);
}
} catch (SocketTimeoutException ignored) {
// Read timed out; socket is good.
} catch (IOException e) {
return false; // Couldn't read; socket is closed.
}
}
return true;
}
isHealthy的判断主要是通过对socket进行判断,判断socket是否关闭、读写是否正常,如果都正常,说明是可用的,否则不可用。另外对http2也进行了判断,通过判断http2Connection是否关闭来判断http2是否能用。
接下来看如果不可用的情况下,noNewStreams()做了什么操作。
public void noNewStreams() {
Socket socket;
Connection releasedConnection;
synchronized (connectionPool) {
releasedConnection = connection;
socket = deallocate(true, false, false);
if (connection != null) releasedConnection = null;
}
//关闭socket
closeQuietly(socket);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
}
private Socket deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {
assert (Thread.holdsLock(connectionPool));
if (streamFinished) {
this.codec = null;
}
if (released) {
this.released = true;
}
Socket socket = null;
if (connection != null) {
if (noNewStreams) {
connection.noNewStreams = true;
}
if (this.codec == null && (this.released || connection.noNewStreams)) {
release(connection);
if (connection.allocations.isEmpty()) {
connection.idleAtNanos = System.nanoTime();
if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {
socket = connection.socket();
}
}
connection = null;
}
}
return socket;
}
可以看出noNewStreams主要调用了dea主要llocate()方法,deallocate()中做的操作主要就是释放资源。
最后来看findConnection方法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
RealConnection result = null;
Route selectedRoute = null;
Connection releasedConnection;
Socket toClose;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
// Attempt to use an already-allocated connection. We need to be careful here because our
// already-allocated connection may have been restricted from creating new streams.
releasedConnection = this.connection;
//当前的connection不能创建新的stream,就置空
toClose = releaseIfNoNewStreams();
//如果当前connection不为空可以直接使用
if (this.connection != null) {
// We had an already-allocated connection and it's good.
result = this.connection;
releasedConnection = null;
}
if (!reportedAcquired) {
// If the connection was never reported acquired, don't report it as released!
releasedConnection = null;
}
//如果当前connection为空,就去连接池里面找一条连接
if (result == null) {
// Attempt to get a connection from the pool.
Internal.instance.get(connectionPool, address, this, null);
if (connection != null) {
foundPooledConnection = true;
result = connection;
} else {
selectedRoute = route;
}
}
}
closeQuietly(toClose);
if (releasedConnection != null) {
eventListener.connectionReleased(call, releasedConnection);
}
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
}
//如果当前的connection可用或者从连接池里面找到了一条可用的connection,那么直接返回
if (result != null) {
// If we found an already-allocated or pooled connection, we're done.
return result;
}
// 到这里说明没有找到可用的连接
// If we need a route selection, make one. This is a blocking operation.
boolean newRouteSelection = false;
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
synchronized (connectionPool) {
if (canceled) throw new IOException("Canceled");
if (newRouteSelection) {
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. This could match due to connection coalescing.
List<Route> routes = routeSelection.getAll();
//切换路由,重新查找
for (int i = 0, size = routes.size(); i < size; i++) {
Route route = routes.get(i);
Internal.instance.get(connectionPool, address, this, route);
if (connection != null) {
foundPooledConnection = true;
result = connection;
this.route = route;
break;
}
}
}
//还没有找到,只能自己创建了
if (!foundPooledConnection) {
if (selectedRoute == null) {
selectedRoute = routeSelection.next();
}
// Create a connection and assign it to this allocation immediately. This makes it possible
// for an asynchronous cancel() to interrupt the handshake we're about to do.
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);
acquire(result, false);
}
}
// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
eventListener.connectionAcquired(call, result);
return result;
}
//执行到这里肯定是自己创建的,开始握手协议
// Do TCP + TLS handshakes. This is a blocking operation.
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
reportedAcquired = true;
// Pool the connection.
Internal.instance.put(connectionPool, result);
// If another multiplexed connection to the same address was created concurrently, then
// release this connection and acquire that one.
if (result.isMultiplexed()) {
socket = Internal.instance.deduplicate(connectionPool, address, this);
result = connection;
}
}
closeQuietly(socket);
eventListener.connectionAcquired(call, result);
return result;
}
findConnection()方法就结束了,代码注释里把几个大的步骤也做了注释,就不在详细的讲解了,因为我对里面的内容也是一知半解,比如路由那块儿。
到此呢,分析就结束了。也不想做总结了,因为感觉写的有点儿乱.....