由于需要,我要不断地调用用OkHttpClient调Https接口(链接),于是有时候会出现下面这个问题:
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.read(InputRecord.java:503)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at okhttp3.internal.io.RealConnection.connectTls(RealConnection.java:239)
at okhttp3.internal.io.RealConnection.establishProtocol(RealConnection.java:196)
at okhttp3.internal.io.RealConnection.buildConnection(RealConnection.java:171)
at okhttp3.internal.io.RealConnection.connect(RealConnection.java:111)
at okhttp3.internal.http.StreamAllocation.findConnection(StreamAllocation.java:187)
at okhttp3.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:123)
at okhttp3.internal.http.StreamAllocation.newStream(StreamAllocation.java:93)
at okhttp3.internal.http.HttpEngine.connect(HttpEngine.java:296)
at okhttp3.internal.http.HttpEngine.sendRequest(HttpEngine.java:248)
at okhttp3.RealCall.getResponse(RealCall.java:243)
at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:201)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163)
at okhttp3.RealCall.execute(RealCall.java:57)
原因是因为:
在服务端/客户端单方面关闭连接的情况下,另一方依然以为
tcp连接仍然建立,试图读取对方的响应数据,导致出现
Software caused connection abort: recv failed的异常.
解决办法是:
在接收数据之前,要先判断连接状态.
在对方释放连接后,也要释放本地的连接.
然而重点是网友们的解决办法都是
通过inputstream的available()方法来判断,是否有响应结果.
如果available()的返回值为0,说明没有响应数据,可能是对方已经断开连接,
如果available()的返回值大于0,说明有响应数据.
可是我们用OkHttp出现这个错误是SocketInputStream来接收数据出错,可以从上面的错误路径中看出。是封装在okhttp3里面的。这又怎么拿SocketInputStream出来呢···黑人???
于是我回想一下我使用的过程,我是不断地调用用OkHttpClient调Https接口才出现这个问题的,单次调用没出现这个错误。而且每次调用都是新建一个OkHttpClient,不断调用上千次的时候就会不断新建对象:
OkHttpClient client = new OkHttpClient().newBuilder().hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();
于是,为什么每次调都要新建一个client 呢,直接用单例模式就好了啊。
下面用枚举的方法实现单例模式:
public enum Client{
INSTANCE;
private OkHttpClient client;
private Client(){
client = new OkHttpClient().newBuilder().hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();
}
public OkHttpClient getInstance() {
return client;
}
}
调用的时候:
Client.INSTANCE.getInstance();
OK,不报错了,但是具体这样为什么能解决,原因还是没能搞清楚,先占个坑,免得以后忘记了,再来补充。