http请求.gif
http请求如上所示:一个请求对应一个响应,一来一回。是一个上层协议,隐藏了许多细节。下面大概流程为:
地址解析 : 解析出协议,端口,主机IP
封装HTTP请求数据包
封装成TCP包,建立TCP连接(tcp三次握手)
客户机发送请求命令
服务器响应
服务器关闭TCP连接
头信息加入了这行代码 Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
注意:http协议中基于tcp/ip
OkHttp对以上步骤的实现解析
okhttp2.gif
RetryAndFollowUpInterceptor创建StreamAllocation对象,处理http的重定向及出错重试。对后续Interceptor的执行的影响为修改Request并创建StreamAllocation对象。
BridgeInterceptor补全缺失的一些http header。对后续Interceptor的执行的影响主要为修改了Request。Connection:keep-alive这里被默认添加
CacheInterceptor处理http缓存。对后续Interceptor的执行的影响为,若缓存中有所需请求的响应,则后续Interceptor不再执行。
ConnectInterceptor借助于前面分配的StreamAllocation对象建立与服务器之间的连接,并选定交互所用的协议是HTTP 1.1还是HTTP 2。对后续Interceptor的执行的影响为,创建了HttpStream和connection。
CallServerInterceptor作为Interceptor链中的最后一个Interceptor,用于处理IO,与服务器进行数据交换。
https://www.jianshu.com/p/5c98999bc34f
使用方法
//1.初始化OKHttp
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.build();
//2.构建request
final Request request = new Request.Builder()
.get()
.url(HttpUrl.parse("http://www.baidu.com/"))
.build();
//3.发送请求(这里并没有执行,只是构建了`RealCall`对象)
Call call = okHttpClient.newCall(request);
//4.接收响应(请求执行,并得到响应结果)
Response execute = call.execute();
//5.输出
System.out.println(execute.body().string());
分析
着重分析步骤3与4,步骤3是为了返回RealCall对象,步骤4则执行的是RealCall.execute()
RealCall.execute
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
RealCall.getResponseWithInterceptorChain
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
.okhttp 地址解析
//解析得到host --- BridgeInterceptor
requestBuilder.header("Host", hostHeader(userRequest.url(), false));
//解析得到主机ip --- StreamAllocation#findConnection
1. StreamAllocation#newStream()
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
2.StreamAllocation#findHealthyConnection()
RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
pingIntervalMillis, connectionRetryEnabled);
3.StreamAllocation#findConnection()
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
routeSelection = routeSelector.next();
}
4.RouteSelector#next()
public Selection next() throws IOException {
...
Proxy proxy = nextProxy();
...
return new Selection(routes);
}
5.RouteSelector#nextProxy()
private Proxy nextProxy() throws IOException {
if (!hasNextProxy()) {
throw new SocketException("No route to " + address.url().host()
+ "; exhausted proxy configurations: " + proxies);
}
Proxy result = proxies.get(nextProxyIndex++);
resetNextInetSocketAddress(result);
return result;
}
6.RouteSelector#resetNextInetSocketAddress()
socketHost = getHostString(proxySocketAddress);
与服务器tcp连接
ConnectInterceptor#intercept
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
findHealthyConnection -> findConnection -> RealConnection#connect -> connectSocket ->
private void connectSocket(int connectTimeout, int readTimeout, Call call,
EventListener eventListener) throws IOException {
Proxy proxy = route.proxy();
Address address = route.address();
rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
? address.socketFactory().createSocket()
: new Socket(proxy);
eventListener.connectStart(call, route.socketAddress(), proxy);
rawSocket.setSoTimeout(readTimeout);
try {
Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
} catch (ConnectException e) {
ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
ce.initCause(e);
throw ce;
}
设置okhttp的超时,这里就可以看得很明白了,其实是设置socket的连接超时与读写超时
.connectTimeout(30*1000, TimeUnit.MILLISECONDS) .readTimeout(30*1000,TimeUnit.MILLISECONDS) .writeTimeout(30*1000,TimeUnit.MILLISECONDS)
发送请求命令
CallServerInterceptor#intercept 这是okhttp中的最后一个入拦截器,主要作用是 与服务器进行数据交换
httpCodec.writeRequestHeaders(request);
Http1Codec#writeRequestHeaders
public void writeRequest(Headers headers, String requestLine) throws IOException {
if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
sink.writeUtf8(requestLine).writeUtf8("\r\n");
for (int i = 0, size = headers.size(); i < size; i++) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n");
}
sink.writeUtf8("\r\n");
state = STATE_OPEN_REQUEST_BODY;
}
这里的IO操作都是OKIO实现