异常: 读取错误: ssl = 0x9524b800: 系统调用期间的 I/O 错误,对等重置连接

在过去的几周里,我们的客户开始看到100多个这样的“ SSLException 错误-通过对等方式重置连接”,我不知道为什么

  1. 我们使用的是没有特殊配置的,带有 okhttp 的 Remofit

    public class OkHttpClientProvider implements IOkHttpClientProvider {
    
    
    OkHttpClient okHttpClient;
    
    
    public OkHttpClientProvider() {
    this.okHttpClient = createClient();
    }
    
    
    public OkHttpClient getOkHttpClient() {
    return this.okHttpClient;
    }
    
    
    private OkHttpClient createClient() {
    return new OkHttpClient();
    }
    }
    

The above client provider is a singleton. The RestAdapter is built using this injected client (we use dagger) -

RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
.setConverter(converter)
.setEndpoint(networkRequestDetails.getServerUrl())
.setClient(new OkClient(okHttpClientProvider.getOkHttpClient()))
.setErrorHandler(new NetworkSynchronousErrorHandler(eventBus))
);

基于堆栈溢出解决方案,我发现

  1. 服务器上的 keep alive 持续时间为180秒,OkHttp 的默认值为300秒

  2. 服务器在其头中返回“ Connection: close”,但客户端请求发送“ Connection: keepAlive”

  3. 服务器支持 TLS 1.0/1.1/1.2,并使用 Open SSL

  4. 我们的服务器已经转移到另一个主机提供商最近在另一个地理,所以我不知道这些是否是 DNS 故障或没有

  5. 我们已经尝试过调整服务器上的 keepAlive、重新配置的 OpenSSL 之类的东西,但由于某种原因,Android 客户机一直得到这个错误

  6. 当你尝试使用应用程序发布内容或者刷新时,它会立即发生,没有任何延迟(它甚至没有进入网络,或者在这个异常发生之前有一个延迟,这意味着连接已经中断)。但是多次尝试以某种方式“修复它”,我们就会获得成功。以后还会发生

  7. 我们已经使服务器上的 DNS 条目无效,以查看是否是这种情况造成了这种情况,但这并没有帮助

  8. 这种情况通常发生在 LTE 上,但我也在 Wifi 上看到过

我不想禁用保持生存,因为大多数现代客户不这样做。而且我们使用的是 OkHttp 2.4,这是后冰淇淋三明治设备的一个问题,所以我希望它能解决这些潜在的网络问题。IOS 客户端也有这些异常,但是少了近100倍(iOS 客户端使用 AFNetworking 2.0)。我正在努力寻找新的尝试,有什么帮助/想法吗?

更新——通过 okhttp 添加完整的堆栈跟踪

      retrofit.RetrofitError: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:390)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.java:76)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:88)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:23)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: javax.net.ssl.SSLException: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:699)
at okio.Okio$2.read(Okio.java:137)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:306)
at okio.RealBufferedSource.indexOf(RealBufferedSource.java:300)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:196)
at com.squareup.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.java:191)
at com.squareup.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.java:80)
at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:917)
at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:793)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:439)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:384)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.java:73)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:38)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
            at java.lang.reflect.Proxy.invoke(Proxy.java:397)
            at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
            at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.java:76)
            at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:88)
            at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.java:23)
            at android.os.AsyncTask$2.call(AsyncTask.java:292)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)
]}
124512 次浏览

最近我在处理一些遗留代码时遇到了这个问题。在谷歌搜索后,我发现这个问题无处不在,但没有任何具体的解决办法。我处理了异常消息的各个部分,并在下面进行了分析。

分析:

  1. SSLException: SSL (安全套接字层)发生异常,这是在 JDK (openJDK/oracleJDK/AndroidSDK)的 javax.net.ssl包中实现的
  2. Read error ssl=# I/O error during system call: 从安全套接字读取时发生错误。这是在使用本机系统库/驱动程序时发生的。请注意,所有 Solaris、 Windows 等平台都有自己的套接字库,SSL 使用这些套接字库。Windows 使用 WINSOCK 库。
  3. Connection reset by peer: 系统库报告此消息(Solaris 报告 ECONNRESET,Windows 报告 WSAECONNRESET) ,数据传输中使用的套接字不再可用,因为现有连接被远程主机强制关闭。需要在主机和客户端之间创建一个新的安全路径

理由:

理解了这个问题,我试图找到连接重置背后的原因,我想出了以下原因:

  • 远程主机上的对等应用程序突然停止,主机重新启动,主机或远程网络接口被禁用,或远程主机使用硬关闭。
  • 如果在一个或多个操作正在进行时,由于保持活动的活动检测到故障而导致连接中断,也可能导致此错误。正在进行的操作在 Network dropped connection on reset(On Windows(WSAENETRESET))中失败,随后的操作在 Connection reset by peer(On Windows(WSAECONNRESET))中失败。
  • 如果目标服务器受到防火墙的保护(这在大多数情况下是正确的) ,则与端口相关的生存时间(TTL)或超时将在给定的超时时间强制关闭 无所事事连接。这是我们感兴趣的东西

决心:

  1. 服务器端的事件,如服务突然停止,重新启动,网络接口禁用无法通过任何方式处理。
  2. 在服务器端,使用较高的 Time to Live (TTL)或超时值(如3600秒)为给定端口配置防火墙。
  3. 客户端可以 “试试”保持网络活动以避免或减少 Connection reset by peer
  4. 正常情况下,正在运行的网络流量使连接保持活跃,并且不经常看到问题/异常。
  5. 在移动网络 2G、3G 和4G中,分组数据传输是间歇性的,并且依赖于移动网络的可用性,它可能不会在服务器端重置 TTL 定时器并且结果为 Connection reset by peer

以下是为解决这一问题而在各种论坛上提出的建议条款

  • ConnectionTimeout:仅在建立连接时使用。如果主机需要时间连接较高的值,这使得客户端等待连接。
  • Socket 超时-表示接收到数据包的最长时间,将连接视为活动的。如果在给定时间内没有接收到数据,则假定连接处于停顿/中断状态。
  • Linger: 当数据排队等待发送并在套接字上调用关闭套接字函数时,到什么时候不应该关闭套接字。
  • TcpNoDelay: 是否要禁用保存和累积 TCP 数据包的缓冲区,并在达到阈值时发送它们?将此值设置为 true 将跳过 TCP 缓冲,以便立即发送每个请求。网络速度的减慢可能是由于网络流量的增加造成的,这是由于数据包传输更小、更频繁造成的。

因此,上述参数都无助于保持网络的活性,因此无效。

我找到了一个可以帮助解决这个问题的设置,那就是这个函数

setKeepAlive(true)
setSoKeepalive(HttpParams params, enableKeepalive="true")

我是怎么解决我的问题的?

  • 设定 HttpConnectionParams.setSoKeepAlive(params, true)
  • 捕获 SSLException并检查 Connection reset by peer的异常消息
  • 如果发现异常,存储下载/读取进度并创建一个新连接。
  • 如果可能的话,重新启动下载/阅读/重新启动下载

我希望细节能帮上忙,编码愉快..。

今天早上我们也遇到了同样的问题,现在解决了,希望这个能帮上忙。

IIS8上的 SSL

  1. 昨天一切正常,昨晚我们的 SSL 在 IIS 站点上进行了更新。
  2. 在签出站点 Bindings to the SSL 时,注意到 IIS8有一个新的复选框 Ref = “ http://www.iis.net/learn/get-start/whats-new-in-iis-8/iis-80-server-name-Indic- sni-ssl-scalability”rel = “ noReferrer”> 需要服务器名称指示,但是在启用它之前没有选中它。
  3. 这引发了问题。
  4. 回到 IIS,关闭复选框... 问题解决! ! !

希望这个能帮上忙! ! !

如果使用 Nginx并遇到类似的问题,那么下面的方法可能会有所帮助:

在此 是的上扫描您的域,并查看设备版本是否允许连接。

如果较低版本的设备(比如 < Android 4.4.2等)由于 TLS 支持而无法连接,那么尝试将其添加到 Nginx 配置文件中,

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

此错误消息的另一个可能原因是 HTTP 方法被服务器或负载平衡器阻塞。

似乎是 标准保安措施阻止未使用的 HTTP 方法。我们遇到这种情况是因为 HEAD 被负载平衡器阻塞了(但是,奇怪的是,并非所有的负载平衡服务器都阻塞了,这导致它只在某些时候出现故障)。通过临时更改为使用 GET 方法,我可以测试请求本身是否工作正常。

IOS 上的错误代码是: 请求应用程序代码时出错: “网络连接丢失”

Android 默认支持 SSL 实现,除了 Android N (API 级别24)和 Android 5.1(API 级别22)以下
在服务器端实现 SSL 之后,我在 API 级别22以下的设备上进行 API 调用时遇到了错误; 那是在创建 OkHttpClient 客户端对象时,通过添加 ConnectionSpecs ()方法 OkHttpClient 修复了这个错误。建造者类。

接收到的错误是

响应失败: javax.net.SSL.SSLException: SSL 握手失败: Ssl = 0xb8882c00: 系统调用期间的 I/O 错误,通过对等方重置连接

所以我修正了这个问题

if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
// Do something for below api level 22
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}

对于 Android N (API 级别24)也是如此; 我在进行 HTTP 调用时得到了这个错误,如下所示

HTTP FAILED: javax.net.ssl.SSLHandshakeException: Handshake 失败

这个问题可以通过添加 Android 7的检查来解决,比如

if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}

所以我的最后一个 OkHttpClient 对象将是:

         OkHttpClient client
HttpLoggingInterceptor httpLoggingInterceptor2 = new
HttpLoggingInterceptor();
httpLoggingInterceptor2.setLevel(HttpLoggingInterceptor.Level.BODY);


OkHttpClient.Builder okb = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor2)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().addHeader(AUTH_KEYWORD, AUTH_TYPE_JW + " " + password).build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS);


if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}


if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}


//init client
client = okb.build();

GetSpecsBelowLollipopMR1函数类似于,

   private List<ConnectionSpec> getSpecsBelowLollipopMR1(OkHttpClient.Builder okb) {


try {


SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
okb.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));


ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();


List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);


return specs;


} catch (Exception exc) {
Timber.e("OkHttpTLSCompat Error while setting TLS 1.2"+ exc);


return null;
}
}

Tls12SocketFactory 类可以在下面的链接中找到(gotev 的评论) :

https://github.com/square/okhttp/issues/2372


为了获得更多的支持,在下面添加一些链接,这将详细地帮助您,

Https://developer.android.com/training/articles/security-ssl

D/OkHttp: < —— HTTP FAILED: javax.net.SSL.SSLException: SSL 握手中止: SSL = 0x64e3c938: 系统调用期间出现 I/O 错误,通过对等方重置连接

我在使用 okhttp/4.0.0-RC1发送网络请求的 Android 5.1.1设备上遇到了这个错误。在服务器端设置头 Content-Length: <sizeof response>解决了这个问题。

我的问题是与 TIMEZONE在模拟器基因运动。改变 TIMEZONE ANDROID EMULATOR等于 TIMEZONE SERVER,解决了问题。

参考文献

优雅地处理异常可能非常困难。

问题: 每当我向我的 Web 服务提出请求时,我都会得到这个 SSLException 异常错误,因为我完全知道我已经用完了我所订阅的互联网数据包,但是我的移动应用程序在检查了 Connectivity.NetworkAccess == NetworkAccess.Internet之后确认我已经连接到了互联网。最糟糕的是,SSLException 异常错误会在数据包耗尽的任何时候导致应用程序崩溃。

在我的案例中,我在测试当有互联网连接时,如果用户试图登录会出什么问题,但是他们使用的特定 互联网服务供应商没有可用的数据包订阅。嗯,应用程序崩溃与 SSLException 异常错误,这是我得到的结果。

我的解决方案: 我在谷歌上搜索了处理这个问题的最佳方法,但是我得到的答案是针对不同的人的,所以我决定弄清楚如何实现我的方法。

首先,作为一般的事情,我围绕我的网络请求代码与 试着接住块,你可以看到下面;

    public static async Task<LoginResponse> Login(string email, string password)
{
try
{
var login = new Login()
{
email_address = email,
password = password
};


var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(login);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(AppBaseUrl.ApiBaseUrl + "Users/LoginUser", content).ConfigureAwait(false);
var jsonResult = await response.Content.ReadAsStringAsync();


var apiResponse = JsonConvert.DeserializeObject<LoginResponse>(jsonResult);
            

return apiResponse;
}
catch (Exception ex)
{
return new LoginResponse
{
code = 0,
responsebody = null,
message = ex.Message
};
}
}

上面行代码的重点在于从 API 返回 Login 响应的 接着块。注: 代码 = 0意味着网络请求没有成功。

在我的 ViewModel中,我添加了以下几行代码;

var loginResponse = await Login(Email, Password);


if (loginResponse.code == 1)
{
// Do something when Login is successful.
}
else
{
// Do something when Login is not successful.
await Application.Current.MainPage.DisplayAlert("Oops!", loginResponse.message + ". Try again", "Ok"); // This line will display a message like "Connection closed by peer. Try again"
}

我附上了我的应用程序在物理 Android 设备上运行的截图。 应用程序的截图

最后的想法: 我仍然相信在这种情况下,我自己的错误处理方法甚至可以得到改进。这个的好处是它的处理跨平台。然而,这个应用程序不会再崩溃了,我希望这能帮助到其他人。