ConnectInterceptor 这个类的主要作用是打开一个到指定的服务器的连接并且向下分发拦截。
构造函数中传入一个OkHttpClinet对象.
public final OkHttpClient client;
public ConnectInterceptor(OkHttpClient client) {
this.client = client;
}
看下它的拦截过程,很简单
@Override public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET. //需要网络请求来满足这个请求,可能用get请求来验证。
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);// 1
RealConnection connection = streamAllocation.connection();// 2
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
- 1处是生成一个HttpCodec对象,这个对象是一个接口,有2个子类,Http1Codec和Http2Codec,Http1Codec中有Socket的输入输出流,主要作用是用于连接时的输入输出,它之中有些必完成的生命周期方法。
- 2处是首先获取一个Http连接对象,streamAllocation这个对象是在RetryAndFollowUpInterceptor中生成。至于这个类是为另一个拦截器CallServerInterceptor使用的,主要作用是向BufferedSink 中写入header数据。
- 生成一个正真的连接对象RealConnection,主要的用处是连接到的serve地址并且在CacheInterceptor中配置CacheStrategy中配置响应的缓存策略。
// Drop the cached response if it's missing a required handshake.
//并且本地缓存缺少必要的握手,将删除本地缓存,则返回不带本地缓存的策略
if (request.isHttps() && cacheResponse.handshake() == null) {
return new CacheStrategy(request, null);
}
下面看下streamAllocation.newStream(client, doExtensiveHealthChecks)方法,
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
int connectTimeout = client.connectTimeoutMillis();
int readTimeout = client.readTimeoutMillis();
int writeTimeout = client.writeTimeoutMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
//找到一个健康的连接
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
得到一个可用的连接,在resultConnection.newCodec(client, this)中根据连接不同生成对应的http解析器对象
public HttpCodec newCodec(
OkHttpClient client, StreamAllocation streamAllocation) throws SocketException {
if (http2Connection != null) {
return new Http2Codec(client, streamAllocation, http2Connection);
} else {
socket.setSoTimeout(client.readTimeoutMillis());
source.timeout().timeout(client.readTimeoutMillis(), MILLISECONDS);
sink.timeout().timeout(client.writeTimeoutMillis(), MILLISECONDS);
return new Http1Codec(client, streamAllocation, source, sink);
}
}
看下findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks)方法。
/**
* Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated
* until a healthy connection is found.
* 找到一个可用的连接,如果这个连接不可用,将会重新查找,直到找到到可用的连接
*/
private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws IOException {
while (true) {
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
connectionRetryEnabled);
// If this is a brand new connection, we can skip the extensive health checks.
// 如果这个一个全新的连接,跳过全面的健康性检查
synchronized (connectionPool) {
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.
// 检查的目的是确认连接是否正常,如果不正常,从连接池中移除,并重新开始建立连接
if (!candidate.isHealthy(doExtensiveHealthChecks)) {
noNewStreams();
continue;
}
return candidate;
}
}
里面的过程是
- 创建无限循环,找到一个连接。
- 如果这个连接是新建的连接就直接是可用。
- 如果确认连接是否可用的,如果不可用就重新查找。
/**
* Returns a connection to host a new stream. This prefers the existing connection if it exists,
* then the pool, finally building a new connection.
* 如果有一个创建好的连接,返回创建好的连接,如果没有这样的连接,那就在pool在中创建,如果还没有,构建一个新的连接。
*
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
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.
// 1 试图使用一个已经配置好了的连接
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// Attempt to get a connection from the pool.
// 2 试图从pool中获取一个链接 ,具体的实现在okhttpclient中
Internal.instance.get(connectionPool, address, this);
if (connection != null) {
return connection;
}
selectedRoute = route;
}
// If we need a route, make one. This is a blocking operation.
// 如果需要一个路由,创建一个
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
}
// 3 如果没有的话就创建一个连接
// 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.
RealConnection result;
synchronized (connectionPool) {
route = selectedRoute;
refusedStreamCount = 0;
result = new RealConnection(connectionPool, selectedRoute);
acquire(result);
if (canceled) throw new IOException("Canceled");
}
// Do TCP + TLS handshakes. This is a blocking operation. 建立握手 ,这是堵塞操作
result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
//移除失败的记录
routeDatabase().connected(result.route());
Socket socket = null;
synchronized (connectionPool) {
// Pool the connection.
//将当前result 放到pool中并进行pool中的整理。
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);
return result;
}
寻找连接的过程:
- 首先是看是否有创建好的连接,如果没有,就在connectionPool 中找,如果还没有就出 创建一个新的连接。
- 进行握手。
- 将新创建的连接放到connectionPool 中,
其中 result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled)的握手就是socket的连接过程。
public void connect(
...
while (true) {
try {
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout);
} else {
connectSocket(connectTimeout, readTimeout);
}
establishProtocol(connectionSpecSelector);
break;
} catch (IOException e) {
...
}
}
在这里也算是okhttp中真正的连接位置。
网友评论