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

nc1teljy  于 2022-12-27  发布在  Java
关注(0)|答案(8)|浏览(652)

在过去的几周里,我们的客户端开始看到数百个这样的“SSLException错误-连接被对等端重置”,我不知道为什么
1.我们正在使用带有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))
                                        );

基于我发现的堆栈溢出解决方案-
1.服务器上的保持活动持续时间为180秒,OkHttp的默认值为300秒
1.服务器返回“连接:close”,但客户端请求发送“Connection:保持活动”
1.服务器支持TLS 1.0 / 1.1 / 1.2并使用Open SSL
1.我们的服务器最近已经转移到另一个托管提供商在另一个地理位置,所以我不知道这些是否是DNS故障或没有
1.我们尝试过调整keepAlive,在服务器上重新配置OpenSSL,但由于某种原因,Android客户端总是收到此错误
1.当你尝试使用应用程序发布内容或进行拉取刷新时,它会立即发生,没有任何延迟(在此异常发生之前,它甚至不会连接到网络或有延迟,这意味着连接已经断开)。但多次尝试以某种方式“修复它”,我们获得了成功。稍后它会再次发生
1.我们已经使服务器上的DNS条目无效,以查看这是否是导致它的原因,但这没有帮助
1.这种情况大多发生在LTE上,但我也在WiFi上看到过
我不想禁用keep alive,因为大多数现代客户端都不这样做。此外,我们使用的是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)
      ]}
mrfwxfqh

mrfwxfqh1#

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

    • 分析:**
  1. SSLException:SSL( ssl 层)发生异常,在JDK(openJDK/oracleJDK/AndroidSDK)的javax.net.ssl包中实现
  2. Read error ssl=# I/O error during system call:从 ssl 读取时出错。使用本机系统库/驱动程序时出错。请注意,所有平台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))**
  • 如果目标服务器受Firewall保护(在大多数情况下都是如此),则与端口关联的生存时间(TTL)或超时将在给定超时时强制关闭 * idle * 连接。这是我们感兴趣的内容
    • 分辨率:**

1.服务器端的事件,如服务突然停止、重新启动、网络接口被禁用,无法通过任何方式进行处理。
1.在服务器端,使用更高的生存时间(TTL)或超时值(如3600秒)为给定端口配置防火墙。
1.客户端可以**"尝试"保持网络活动以避免或减少Connection reset by peer
1.正常情况下,持续的网络流量会使连接保持活动状态,并且不会频繁出现问题/异常。
Strong Wifi has least chances ofConnection reset by peer``**。
1.对于分组数据传送是间歇性的并且取决于移动网络可用性的移动网络2G, 3G and 4G,它可能不重置服务器侧的TTL定时器,并且导致Connection reset by peer
以下是建议在各种论坛上设置以解决该问题的条件

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

因此,上述参数中没有一个有助于保持网络活动,因此是无效的。
我发现一个设置可能有助于解决这个问题,这是这个功能

setKeepAlive(true)
setSoKeepalive(HttpParams params, enableKeepalive="true")
    • 我如何解决我的问题?**
  • 设置HttpConnectionParams.setSoKeepAlive(params, true)
  • 捕获SSLException并检查Connection reset by peer的异常消息
  • 如果发现异常,则保存下载/读取进度并创建新连接。
  • 如果可能,恢复下载/读取,否则重新启动下载
4ngedf3f

4ngedf3f2#

如果使用Nginx遇到类似的问题,那么以下方法可能会有所帮助:
在此sslTesturl上扫描您的域,查看您的设备版本是否允许连接。
如果较低版本的设备(如〈Android 4.4.2等)无法连接,由于TLS支持,然后尝试将此添加到您的Nginx配置文件,

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
jmo0nnb3

jmo0nnb33#

从今天早上开始我们也遇到了同样的问题,现在已经解决了...希望这能有所帮助...
IIS 8上的SSL
1.昨天一切正常,昨晚我们的SSL在IIS站点上更新了。
1.在 checkout 站点绑定到SSL时,注意到IIS 8有一个新的复选框 * Require Server Name Indication *,在启用之前未选中该复选框。
1.这引发了问题。
1.返回IIS,禁用复选框....问题解决!!!
希望这对你有帮助!

llycmphe

llycmphe4#

Android默认支持SSL实现,Android N(API级别24)及以下版本(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/OK HTTP:〈--HTTP失败:javax.net.ssl.SSLException: SSL握手已中止:ssl = 0x64e3c938:系统调用期间发生I/O错误,对等方重置连接

ltqd579y

ltqd579y5#

出现此错误消息的另一个可能原因是HTTP方法被服务器或负载平衡器阻止。
似乎是standard security practice阻止了未使用的HTTP方法。我们遇到了这个问题,因为HEAD被负载平衡器阻止了(但奇怪的是,不是所有的负载平衡服务器,这导致它只在某些时候失败)。我能够测试请求本身是否工作正常,方法是临时将其更改为使用GET方法。
iOS上的错误代码为:请求应用程序代码时出错:错误域=NSURLErrorDomain代码=-1005“网络连接丢失。”

ar7v8xwq

ar7v8xwq6#

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

bgibtngc

bgibtngc7#

我的问题是与TIMEZONE在模拟器genymotion。改变TIMEZONE ANDROID EMULATOR等于TIMEZONE SERVER,解决了问题。
reference

niknxzdl

niknxzdl8#

优雅地处理异常可能非常困难。
问题:每当我向Web服务发出请求时,我都会遇到此javax.net.ssl.SSLException错误,我完全清楚自己已用尽订阅的Internet数据包,但我的移动的应用程序在检查Connectivity.NetworkAccess == NetworkAccess.Internet后确认我已连接到Internet。最糟糕的是,javax.net.ssl.SSLException错误会在数据包用尽时使应用程序崩溃。
我的案例:在我的案例中,我正在测试如果用户在有互联网连接的情况下尝试登录,但他们正在使用的特定ISP没有可用的数据包订阅,会出现什么问题。嗯,应用程序崩溃并出现javax.net.ssl.SSLException错误,这就是我得到的结果。
**我的解决方案:**我在谷歌上搜索了处理这个问题的最佳方法,但我得到的答案是针对不同的人,所以我决定找出如何实施我的。

首先,作为一般的事情,我用try-catch块包围了我的Web请求代码,如下图所示;

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表示Web请求不成功。
在我的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

**最后的想法:**我仍然相信我自己的错误处理方法在这种情况下甚至可以改进。这是什么好的是,它的跨平台处理。然而,应用程序不会崩溃了,我希望这有助于有人在那里。

相关问题