javax.net.ssl.SSLException:读取错误:ssl=0x9524b800:系统调用期间的 I/O 错误,对等方重置连接

Posted

技术标签:

【中文标题】javax.net.ssl.SSLException:读取错误:ssl=0x9524b800:系统调用期间的 I/O 错误,对等方重置连接【英文标题】:javax.net.ssl.SSLException: Read error: ssl=0x9524b800: I/O error during system call, Connection reset by peer 【发布时间】:2015-08-12 20:31:12 【问题描述】:

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

    我们使用的是带有okhttp的Retrofit,没有特殊配置

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

上面的客户端提供者是一个单例。 RestAdapter 是使用这个注入的客户端构建的(我们使用 dagger) -

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

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

    服务器的存活时间为180秒,OkHttp默认为300秒

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

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

    我们的服务器最近已转移到另一个地理位置的另一个托管服务提供商,所以我不知道这些是否是 DNS 故障

    我们尝试过调整 keepAlive 等内容,在服务器上重新配置 OpenSSL,但由于某种原因,android 客户端不断收到此错误

    当您尝试使用该应用发布内容或拉取刷新时,它会立即发生,没有任何延迟(它甚至不会进入网络或在此异常发生之前有延迟,这意味着连接已经破碎的)。但是多次尝试以某种方式“修复它”并且我们获得了成功。以后会再次发生

    我们已使服务器上的 DNS 条目无效,以查看是否是导致它的原因,但没有帮助

    它主要发生在 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)
      ]

【问题讨论】:

问题是在OkHttp 2.3下出现的吗?我们试图在 2.4 中改进连接性,但您可能遇到了我们退步的情况。 你没有用setRetryOnConnectionFailure()配置你的OkHttpClient是吗? 嗨@JesseWilson,不是吗?以前没做过,我可以试试 我在尝试将多部分上传到 AWS 时也遇到了 SSLException,这是日志的粘贴 pastebin.com/My63htPg (ok v 2.4.0) 有同样的问题。这里有什么解决方案吗? 【参考方案1】:

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

分析:

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

原因:

了解问题后,我尝试查找连接重置背后的原因,我想出了以下原因:

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

分辨率:

    无法以任何方式处理服务器端的事件,例如服务突然停止、重新启动、网络接口禁用。 在服务器端,为给定端口配置防火墙,使用较高的生存时间 (TTL) 或超时值(例如 3600 秒)。 客户端可以“尝试”保持网络活跃以避免或减少Connection reset by peer。 正常情况下,持续的网络流量使连接保持活动状态,并且不会经常出现问题/异常。 强 Wifi 出现 Connection reset by peer 的机会最少。 在移动网络 2G、3G 和 4G 中,数据包数据传输是间歇性的并且取决于移动网络的可用性,它可能不会重置服务器端的 TTL 计时器并导致@ 987654332@。

以下是建议在各种论坛上设置以解决问题的条款

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

所以上述参数都不能帮助保持网络活跃,因此无效。

我找到了一个可以帮助解决这个问题的设置

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

我是如何解决我的问题的?

设置HttpConnectionParams.setSoKeepAlive(params, true) 捕获SSLException 并检查Connection reset by peer 的异常消息 如果发现异常,存储下载/读取进度并创建新连接。 如果可能,继续下载/读取,否则重新开始下载

我希望细节有所帮助。快乐编码...

【讨论】:

这是一个很好的答案。我们的经历是相似的,虽然不一样。我们在客户端和服务器上都按照相同的思路进行了多次调整。我们的错误率现在已经降低到比以前低 100 倍左右,尽管它们仍然没有消失。我仍然没有一个好的答案来更新这个帖子 强/好 wifi 是我留下的一个问题,所以我通过增加重试时间和尝试次数来解决这个问题。也知道整个过程失败时会抛出哪种异常,当它工作时,volley也给出了很好的输出 我面临同样的问题,我的应用程序开发人员使用 react native。那么如何在 react native 代码中解决这个问题呢?【参考方案2】:

如果使用nginx 并遇到类似问题,那么这可能会有所帮助:

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

如果较低版本的设备(如

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

【讨论】:

谁说过 Nginx?如果不允许连接设备,它就无法连接,但这不是问题 - Rickster 说:“多次尝试以某种方式“修复它”,我们获得了成功。 哦,是的,你是对的。我的意思是,如果使用 Nginx 并遇到类似的错误,那么这将解决。 :)【参考方案3】:

从今天早上开始,我们遇到了同样的问题,并且解决了...希望这会有所帮助...

IIS 8 上的 SSL

    昨天一切正常,昨晚我们在 IIS 站点上更新了 SSL。 在检查站点绑定到 SSL 时,发现 IIS8 有一个新复选框 Require Server Name Indication,但没有选中它,所以先启用它。 这引发了问题。 回到 IIS,禁用复选框.... 问题已解决!!!!

希望对你有帮助!!!

【讨论】:

【参考方案4】:

Android 默认支持 SSL 实现,Android N(API 级别 24)和 Android 5.1 以下(API 级别 22)除外 在服务器端实现 SSL 后,在 API 级别 22 设备以下进行 API 调用时出现错误;那是在创建 OkHttpClient 客户端对象时,并通过添加 connectionSpecs() 方法 OkHttpClient.Builder 类来修复。

收到的错误是

响应失败: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 失败:javax.net.ssl.SSLHandshakeException:握手失败

这可以通过添加对 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 handshake aborted: ssl=0x64e3c938: I/O error during system call, Connection reset by peer

【讨论】:

【参考方案5】:

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

似乎是standard security practice 来阻止未使用的 HTTP 方法。我们遇到了这个问题,因为 HEAD 被负载均衡器阻塞(但奇怪的是,不是所有的负载均衡服务器,这导致它仅在某些时候失败)。通过临时更改请求以使用 GET 方法,我能够测试请求本身是否正常工作。

iOS 上的错误代码是: 请求应用代码时出错:Error Domain=NSURLErrorDomain Code=-1005 “网络连接丢失。”

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

我的问题在于模拟器 genymotion 中的 TIMEZONE。改TIMEZONE ANDROID EMULATOR等于TIMEZONE SERVER,问题解决。

reference

【讨论】:

【参考方案8】:

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

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

我的案例: 就我而言,我正在测试如果用户在有互联网连接时尝试登录会出现什么问题,但他们正在使用的特定 ISP 没有可用的数据包订阅。好吧,应用程序因 javax.net.ssl.SSLException 错误而崩溃,这就是我得到的结果。

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

首先,一般来说,我用 try-catch 块包围了我的网络请求代码,如下所示;

    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
            ;
        
    

以上代码的重点是在 Catch 块中,我从我的 API 返回登录响应。 注意: code = 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 设备上运行的屏幕截图。 Screenshot of the app

最后的想法: 我仍然相信在这种情况下我自己的错误处理方法甚至可以改进。这样做的好处是它可以跨平台处理。但是,该应用程序不再崩溃,我希望这对那里的人有所帮助。

【讨论】:

以上是关于javax.net.ssl.SSLException:读取错误:ssl=0x9524b800:系统调用期间的 I/O 错误,对等方重置连接的主要内容,如果未能解决你的问题,请参考以下文章