接上篇。
(主要从网络拉取响应分析)从sendRequest方法中可以看到
httpStream = connect();
httpStream.setHttpEngine(this);
接下来,我们就看看connect()方法:
HttpEngine#connect
private HttpStream connect() throws RouteException, RequestException, IOException {
boolean doExtensiveHealthChecks = !networkRequest.method().equals("GET");
return streamAllocation.newStream(client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis(),
client.retryOnConnectionFailure(), doExtensiveHealthChecks);
}
继续跟踪
StreamAllocation#newStream
public HttpStream newStream(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
throws RouteException, IOException {
try {
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
HttpStream resultStream;
if (resultConnection.framedConnection != null) {
resultStream = new Http2xStream(this, resultConnection.framedConnection);
} else {
resultConnection.socket().setSoTimeout(readTimeout);
resultConnection.source.timeout().timeout(readTimeout, MILLISECONDS);
resultConnection.sink.timeout().timeout(writeTimeout, MILLISECONDS);
resultStream = new Http1xStream(this, resultConnection.source, resultConnection.sink);
}
synchronized (connectionPool) {
stream = resultStream;
return resultStream;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
可以看到这里创建了RealConnection resultConnection 对象。
具体创建的代码
StreamAllocation#findHealthyConnection
/**
* 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, RouteException {
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;
}
}
// Otherwise do a potentially-slow check to confirm that the pooled connection is still good.
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate;
}
connectionFailed(new IOException());
}
}
继续跟踪
StreamAllocation#findConnection
/**
* Returns a connection to host a new stream. This prefers the existing connection if it exists,
* then the pool, finally building a new connection.
*/
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
boolean connectionRetryEnabled) throws IOException, RouteException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (stream != null) throw new IllegalStateException("stream != null");
if (canceled) throw new IOException("Canceled");
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// Attempt to get a connection from the pool.
//如果连接池中已经存在连接,就从中取出(get)RealConnection
RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this);
if (pooledConnection != null) {
this.connection = pooledConnection;
return pooledConnection;
}
selectedRoute = route;
}
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
synchronized (connectionPool) {
route = selectedRoute;
}
}
RealConnection newConnection = new RealConnection(selectedRoute);
acquire(newConnection);
//将建立成功的RealConnection放入(put)连接池缓存
synchronized (connectionPool) {
Internal.instance.put(connectionPool, newConnection);
this.connection = newConnection;
if (canceled) throw new IOException("Canceled");
}
//根据选择的路线(Route),调用Platform.get().connectSocket选择当前平台Runtime下最好的socket库进行握手
newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
connectionRetryEnabled);
routeDatabase().connected(newConnection.route());
return newConnection;
}
到这里我们先介绍Socket管理(StreamAllocation)。
进行HTTP连接需要进行Socket握手,Socket握手的前提是根据域名或代理确定Socket的ip与端口。这个环节主要讲了http的握手过程与连接池的管理,分析的对象主要是StreamAllocation
此步骤用于获取socket的ip与端口,各位请欣赏源码中next()的迷之缩进与递归,代码进行了如下事情:
如果Proxy为null:
如果Proxy为HTTP:
当地址,端口准备好了,就可以进行TCP连接了(也就是我们常说的TCP三次握手),步骤如下:
如果不再需要(比如通信完成,连接失败等)此链路后,释放连接(也就是TCP断开的握手)
经过上述分析,相信大家都有了一定的概念,对上面的那段源码也就看的很自然。如果连接池中已经存在连接,就从中取出(get)RealConnection,如果没有命就根据选择的路线(Route),调用Platform.get().connectSocket选择当前平台Runtime下最好的socket库进行握手。
RealConnection#connect
public void connect(int connectTimeout, int readTimeout, int writeTimeout,
List connectionSpecs, boolean connectionRetryEnabled) throws RouteException {
if (protocol != null) throw new IllegalStateException("already connected");
RouteException routeException = null;
ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
Proxy proxy = route.proxy();
Address address = route.address();
if (route.address().sslSocketFactory() == null
&& !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
throw new RouteException(new UnknownServiceException(
"CLEARTEXT communication not supported: " + connectionSpecs));
}
while (protocol == null) {
try {
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector);
} catch (IOException e) {
closeQuietly(socket);
closeQuietly(rawSocket);
socket = null;
rawSocket = null;
source = null;
sink = null;
handshake = null;
protocol = null;
if (routeException == null) {
routeException = new RouteException(e);
} else {
routeException.addConnectException(e);
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException;
}
}
}
}
如果存在TLS,就根据SSL版本与证书进行安全握手
RealConnection#connectSocket
/** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
private void connectSocket(int connectTimeout, int readTimeout, int writeTimeout,
ConnectionSpecSelector connectionSpecSelector) throws IOException {
rawSocket.setSoTimeout(readTimeout);
try {
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));
sink = Okio.buffer(Okio.sink(rawSocket));
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.sendConnectionPreface();
// Only assign the framed connection once the preface has been sent successfully.
this.allocationLimit = framedConnection.maxConcurrentStreams();
this.framedConnection = framedConnection;
} else {
this.allocationLimit = 1;
}
}
上面这段代码写的就是根据选择的路线(Route),调用Platform.get().connectSocket选择当前平台Runtime下最好的socket库进行握手。
这里又引入了一概念。HTTP请求序列化/反序列化
下面我们就来分析分析。
分析的对象是HttpStream接口,在HTTP/1.1下是Http1xStream实现的。
以下为无缓存,无多次302跳转,网络良好,HTTP/1.1下的GET访问实例分析。
我们已经在上文的RealConnection通过connectSocket()构造HttpStream对象并建立套接字连接(完成三次握手)
在connect()有非常重要的一步,它通过okio库与远程socket建立了I/O连接,为了更好的理解,我们可以把它看成管道
//source 用于获取response
source = Okio.buffer(Okio.source(rawSocket));
//sink 用于write buffer 到server
sink = Okio.buffer(Okio.sink(rawSocket));
我们通过Request.Builder构建了简陋的请求后,可能需要进行一些修饰,这时需要使用Interceptors对Request进行进一步的拼装了。
拦截器是okhttp中强大的流程装置,它可以用来监控log,修改请求,修改结果,甚至是对用户透明的GZIP压缩。类似于函数式编程中的flatmap操作。在okhttp中,内部维护了一个Interceptors的List,通过InterceptorChain进行多次拦截修改操作。
源代码中是自增递归(recursive)调用Chain.process(),直到interceptors().size()中的拦截器全部调用完。主要做了两件事:
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
//递归调用Chain.process()
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null");
}
return interceptedResponse;
}
// No more interceptors. Do HTTP.
return getResponse(request, forWebSocket);
}
接下来是正式的网络请求getResponse(),此步骤通过http协议规范将对象中的数据信息序列化为Raw文本:
此步骤根据获取到的Socket纯文本,解析为Response对象,我们可以看成是一个反序列化(通过http协议将Raw文本转成对象)的过程:
拦截器的设计:
网络读取(readNetworkResponse)分析:
接下来进行释放socket连接,上文已经介绍过了。现在我们就获得到response对象,可以进行进一步的Gson等操作了。
到目前,基本框架也算是介绍完了。还是那句话,共同学习与进步。