从okhttp之拦截器中,我们知道建立连接是在ConnectInterceptor中完成的,今天具体分析一下实现细节
在默认情况下,是没有现成的连接使用的,所以从新建开始看
一、建立连接
val newConnection = RealConnection(connectionPool, route)
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
新建了RealConnection对象,调用connect方法
fun connect() {
while (true) {
try {
// 1 HTTPS
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
} else {
// 2 HTTP
connectSocket(connectTimeout, readTimeout, call, eventListener)
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
break
}
}
}
两种情况分开分析
1、HTTPS连接建立
进入connectTunnel
private fun connectTunnel() {
//构建不带敏感字段的请求,用来打通隧道
var tunnelRequest: Request = createTunnelRequest()
val url = tunnelRequest.url
for (i in 0 until MAX_TUNNEL_ATTEMPTS) {
connectSocket(connectTimeout, readTimeout, call, eventListener)
tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url)
?: break
}
}
创建建立隧道用的request,然后调用connectSocket:
private fun connectSocket() {
//创建套接字
val rawSocket = when (proxy.type()) {
Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
else -> Socket(proxy)
}
//连接套接字
try {
Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
}
//创建用来与服务器通信的通道,okio实现
source = rawSocket.source().buffer()
sink = rawSocket.sink().buffer()
}
可以看到是新建了一个套接字,然后调用connect方法。这里开始创建了TCP的连接,接下来就是创建SSL隧道(因为SSL是应用层连接,我们也可以从这里看出来SSL是架设在TCP之上的,而这种分层的协议也很方便添加应用层协议上去。),开始进入createTunnel方法:
private fun createTunnel(): Request? {
var nextRequest = tunnelRequest
//建立SSL协议的字段
val requestLine = "CONNECT ${url.toHostHeader(includeDefaultPort = true)} HTTP/1.1"
while (true) {
val source = this.source!!
val sink = this.sink!!
val tunnelCodec = Http1ExchangeCodec(null, this, source, sink)
tunnelCodec.writeRequest(nextRequest.headers, requestLine)
tunnelCodec.finishRequest()
val response = tunnelCodec.readResponseHeaders(false)!!
.request(nextRequest)
.build()
tunnelCodec.skipConnectBody(response)
when (response.code) {
//成功
HTTP_OK -> {
return null
}
//需要授权
HTTP_PROXY_AUTH -> {
if ("close".equals(response.header("Connection"), ignoreCase = true)) {
return nextRequest
}
}
}
}
}
如果需要认证身份的话,需要重复发请求。返回去看剩下的方法establishProtocol:
private fun establishProtocol() {
//sslSocketFactory在HTTP协议的时候为空,HTTPS不为空
if (route.address.sslSocketFactory == null) {
//如果有h2_prior_knowledge字段,调用启动HTTP2
if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
socket = rawSocket
protocol = Protocol.H2_PRIOR_KNOWLEDGE
startHttp2(pingIntervalMillis)
return
}
//HTTP1.1
protocol = Protocol.HTTP_1_1
return
}
//TLS握手
connectTls(connectionSpecSelector)
if (protocol === Protocol.HTTP_2) {
startHttp2(pingIntervalMillis)
}
}
假如支持HTTP2,启动HTTP2,否则是HTTP1.1,然后是TLS握手,HTTP2的部分我们不做展开,这里看一下TLS握手:
private fun connectTls(connectionSpecSelector: ConnectionSpecSelector) {
val address = route.address
val sslSocketFactory = address.sslSocketFactory
var success = false
var sslSocket: SSLSocket? = null
try {
//创建SSL套接字
sslSocket = sslSocketFactory!!.createSocket(
rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket
//密码对以及扩展等的配置
val connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
if (connectionSpec.supportsTlsExtensions) {
Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)
}
//开始握手
sslSocket.startHandshake()
// block for session establishment
val sslSocketSession = sslSocket.session
val unverifiedHandshake = sslSocketSession.handshake()
//验证证书
if (!address.hostnameVerifier!!.verify(address.url.host, sslSocketSession)) {
val peerCertificates = unverifiedHandshake.peerCertificates
if (peerCertificates.isNotEmpty()) {
throw SSLPeerUnverifiedException()
} else {
throw SSLPeerUnverifiedException()
}
}
//握手成功,保存协议
val maybeProtocol = if (connectionSpec.supportsTlsExtensions) {
Platform.get().getSelectedProtocol(sslSocket)
} else {
null
}
socket = sslSocket
//更新数据通道
source = sslSocket.source().buffer()
sink = sslSocket.sink().buffer()
}
}
这里实现了TLS的证书验证过程,握手成功后输入和输出通道变成SSL套接字的输入以及输出,也代表我们交流的对象变成了SslSocket,完成了HTTPS的连接建立
然后返回去看一下HTTP的连接建立case
2、HTTP连接建立
调用到了connectSocket方法,这个方法我们已经分析过了,就是socket的连接,实现了TCP的三次握手,只是具体握手细节是系统实现的,我们只需要调用connect就行,最后是establishProtocol,如果没有HTTP2的话,只是对protocal的赋值。
二、总结
除了应用层本身的协议SSL之外,应用层对于连接的建立仅限于对系统接口的调用。
注:由于目前我对HTTP2不甚了解,贸然读源码没有什么实际的意义,所以这里没有做展开