如何查看 OkHttp 客户端的完整内部工作?改造插座关闭超时

Posted

技术标签:

【中文标题】如何查看 OkHttp 客户端的完整内部工作?改造插座关闭超时【英文标题】:How can i see The full Inner Working of an OkHttp Client? Retrofit Socket Closed Time Out 【发布时间】:2020-05-28 07:04:58 【问题描述】:

您好,非常感谢您抽出宝贵时间,我有一个未知的改装问题,我唯一的线索是套接字关闭/超时消息,所以我想我可以帮助我了解更多关于 okhttp 客户端的信息和改造工作,因为我知道套接字必须对 HTTP 客户端做更多的事情,所以我想更多地了解 okhttp 客户端背后的内部工作,因为这可能会导致解决方案。

所以这是我的问题。

我遇到的一个问题是,我的一个回调函数在 android 应用程序上通过 Post 获取一些数据,即使同一 Web 服务中的其他调用稳定且工作正常,我的一个回调也永远不会返回响应正文。这个错误是相当随机的,它只会在 Android 应用程序上使用改造时发生,而使用邮递员或在 Web 浏览器上使用 API 工作得很好。

我能够始终如一地复制错误的唯一方法是将 android 模拟器上的网络类型从 Full 切换到 HSDPA 或任何其他不同于 full 或 LTE 的网络类型。

<blockquote class="imgur-embed-pub" lang="en" data-id="a/8bgCxtr" data-context="false" ><a href="//imgur.com/a/8bgCxtr"></a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>

这是我通过 Throwable on Failure Calls 得到的一些响应

java.net.SocketTimeoutException: 超时 详细信息:超时

我尝试增加 OkHttp 客户端上的超时,而不是超时错误,我得到一个 Socket Closed。 java.net.SocketException:套接字关闭 详细信息:套接字关闭

除了那些简单的行之外,没有更多关于错误的信息,所以我搜索了一种获取更多信息的方法,我发现我可以在 OkHttp 客户端上设置一个日志拦截器来获取有关该问题的更多信息,但是我得到了什么还不足以让我看到可能的解决方案。

这是我在调用正确返回时得到的日志:

D/OkHttp: --> POST http://192.168.1.68:83/api/Comercios/comercios

D/OkHttp: Content-Type: application/json; charset=UTF-8

D/OkHttp:内容长度:418

D/OkHttp:授权:承载 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfVXNlck5hbWUiOiJVU0VSX0JPTkFOWkExIiwiX0hhc2giOiI1U1FGQUEiLCJqdGkiOiIzNmNmYTZlOS01ODk3LTQ0NjYtYjA0Mi1kZGVkMDBiZGU5ZDAiLCJleHAiOjE1ODE0ODAxMjUsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODMiLCJhdWQiOiJNeVRlc3RBcGkifQ.9xAkkMxRpUEFMNdQaZvVTYQIEc-8w22fl_ubIAVaqi4 P>

D/OkHttp: “anyObjects”: “idGrupo”: “00d60025-6fb9-484f-97f0-08d79dfdbf92”, “文化之”: “”, “散列”: “5SQFAA”, “标记”:“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfVXNlck5hbWUiOiJVU0VSX0JPTkFOWkExIiwiX0hhc2giOiI1U1FGQUEiLCJqdGkiOiIzNmNmYTZlOS01ODk3LTQ0NjYtYjA0Mi1kZGVkMDBiZGU5ZDAiLCJleHAiOjE1ODE0ODAxMjUsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODMiLCJhdWQiOiJNeVRlc3RBcGkifQ.9xAkkMxRpUEFMNdQaZvVTYQIEc-8w22fl_ubIAVaqi4 ","usuario":"USER_BONANZA1"

D/OkHttp: --> END POST (418-byte body)

D/OkHttp: http://192.168.1.68:83/api/Comercios/comercios (66 毫秒)

D/OkHttp:传输编码:分块

D/OkHttp: Content-Type: application/json;字符集=utf-8

D/OkHttp:服务器:Kestrel

D/OkHttp: X-Powered-By: ASP.NET

D/OkHttp:日期:2020 年 2 月 12 日,星期三,格林威治标准时间 01:03:01

https://pastebin.com/wRwnMrsk2020-02-11 19:02:59.515

D/OkHttp:

响应正文在粘贴箱中,太长,无法出现在此帖子中。

这是调用超时或套接字关闭的时候

D/OkHttp: --> POST http://192.168.1.68:83/api/Comercios/comercios D/OkHttp: Content-Type: application/json;字符集=UTF-8 D/OkHttp: 内容长度:418 D/OkHttp:授权:承载 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfVXNlck5hbWUiOiJVU0VSX0JPTkFOWkExIiwiX0hhc2giOiJCRFo1R0EiLCJqdGkiOiI0NmI0MTA0NC03M2M2LTQ5MDEtOTcyMy0yZjE1NjE1ODgzZDgiLCJleHAiOjE1ODE0ODAzMjMsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODMiLCJhdWQiOiJNeVRlc3RBcGkifQ.EgVFlAzqOlV-RpjRRxXpQzp3q3kk1FqTcoaGDQBY0XA P>

D/OkHttp: “anyObjects”: “idGrupo”: “00d60025-6fb9-484f-97f0-08d79dfdbf92”, “文化之”: “”, “散列”: “BDZ5GA”, “标记”:“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfVXNlck5hbWUiOiJVU0VSX0JPTkFOWkExIiwiX0hhc2giOiJCRFo1R0EiLCJqdGkiOiI0NmI0MTA0NC03M2M2LTQ5MDEtOTcyMy0yZjE1NjE1ODgzZDgiLCJleHAiOjE1ODE0ODAzMjMsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODMiLCJhdWQiOiJNeVRlc3RBcGkifQ.EgVFlAzqOlV-RpjRRxXpQzp3q3kk1FqTcoaGDQBY0XA ","usuario":"USER_BONANZA1"

D/OkHttp: --> END POST (418-byte body)

D/OkHttp: http://192.168.1.68:83/api/Comercios/comercios (34 毫秒)

D/OkHttp:传输编码:分块

D/OkHttp: Content-Type: application/json;字符集=utf-8

D/OkHttp:服务器:Kestrel

D/OkHttp: X-Powered-By: ASP.NET

D/OkHttp:日期:2020 年 2 月 12 日,星期三,格林威治标准时间 01:05:45

它只是返回一个空的主体,没有其他解释原因。

在搜索socket closed error或者time out的时候,大多都是死胡同,说是和服务端有关,和应用无关,我和管理API和服务端的人聊了聊,时间和再一次有人告诉我,从他们这边看没有错,至少他们知道,他们提到的一件事是,可能正文的字节大小太大,这就是为什么不允许随机关闭套接字的原因,但我不知道如何保持打开状态,或者这是否是首要问题。

所以我想知道是否有办法查看有关 Okhttp 服务器的更详细日志,以便更好地搜索可能的解决方案。

我将在这里发布我的代码,我发送到 Web 服务的模型很好,这不是问题,因为如果结构不是服务器期望的那样,我会收到一个 onSuccess 响应,其中包含我的详细错误从服务器做错了,只是当一切正常时,我收到超时或套接字关闭,有时它工作有时它没有(根据邮递员和我的同行,不是服务器问题)但它永远不会工作当我在android模拟器上切换网络类型时,我已经在帖子中显示过。

这是我的 Gradle 实现


implementation "com.squareup.retrofit2:retrofit:2.5.0"
implementation "com.squareup.retrofit2:converter-gson:2.3.0"
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'

这就是我构建客户端和改造的方式


private fun buildRetrofit(): Retrofit 

    val logger = HttpLoggingInterceptor()
    logger.setLevel(HttpLoggingInterceptor.Level.BODY)

    val client = OkHttpClient()
        .newBuilder()
        .addInterceptor(logger)
        .build()

    return Retrofit.Builder()
        .baseUrl(FN.returnWebAPI(this))
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build()


这就是我拨打电话的方式,现在只是接收响应正文以确保它与数据解析无关。


val idGrupoComercios = hashMapOf("idGrupo" to id)
                val hashComercio = HashComercio(hash, upperU.usuario.toUpperCase(Locale.getDefault()), "", jwt.toString(), idGrupoComercios
                )
            val callComercios: Call<ResponseBody> =
                service.getComercios(tokenBearer, hashComercio)

            callComercios.enqueue(object :
                Callback<ResponseBody> 
                override fun onFailure(
                    call: Call<ResponseBody>,
                    t: Throwable
                ) 
                    Crashlytics.logException(t)

                

                override fun onResponse(
                    call: Call<ResponseBody>,
                    response: Response<ResponseBody>
                ) 
                    if (response.code() == 200) 
                       val data = response.body()!!

                

            )

所以任何关于我能做什么的帮助或想法都会有很大的帮助,我现在似乎可以从项目的应用程序方面做一些事情,所以任何帮助都会很棒。

【问题讨论】:

可能使用pastebin.com 粘贴完整日志(删除了一些内容)并分享链接。理想情况下,完整的日志可以帮助某人查看正在发生的事情。 完整的 HtttpLoginterceptor 日志在我上面发布的 pastebin 上,pastebin.com/wRwnMrsk 但是如果您输入 Logcat 或 Java 控制台日志,这就是控制台没有给我更多信息的问题,至少使用方式它。日志只有两行 Socket Closed。 java.net.SocketException: Socket closed detail message: Socket closed that't it no more info ,这就是为什么我正在寻找一种获取更多信息的方法。 【参考方案1】:

我们能够找到解决问题的方法。 过去,我尝试的最长超时时间是构建客户端时的 5 分钟,但是如果您不将其最大化,它构建的套接字似乎会关闭,因此我将超时更改为一天,这样它就会最大化可能需要超时。

Android 系统或服务器实际上不会等待一天的响应,因此,在大多数情况下,如果服务器没有重置连接,它会发送响应。

java.net.SocketException:连接重置

在这些情况下,您只需在 retryOnConnectionFailure 上设置 true,即使返回答案需要更多时间,它也会提供答案。

我与管理服务器的人交谈并告诉我问题是 HTTP 客户端或改造在第一次尝试时无法重建消息,如果响应正文太长,后续尝试更有可能失败使用 3g 连接,如果它在 json 中包含图像或哈希值,就像我正在做的那样 将完整图像的哈希放在 json 上。

这是客户端的代码

val client = OkHttpClient.Builder()
            .connectTimeout(1, TimeUnit.DAYS)
            .readTimeout(1, TimeUnit.DAYS)
            .writeTimeout(1, TimeUnit.DAYS)
            .retryOnConnectionFailure(false)
            .callTimeout(1, TimeUnit.DAYS)
            .hostnameVerifier(HostnameVerifier  hostname, session -> true )
            .build()

他还告诉我,为了获得最佳结果,使用 HSDPA 时的 json 应小于 1024 字节,包括标头和所有内容。最多 2024 字节的响应正文。

【讨论】:

以上是关于如何查看 OkHttp 客户端的完整内部工作?改造插座关闭超时的主要内容,如果未能解决你的问题,请参考以下文章

OkHttp/Retrofit 默认超时

改造 OkHttp SSLHandshakeException

使用 okhttp 客户端的不良缓冲

离线时使用 OKHttp 进行改造如何使用缓存数据

使用改造 2 将图像从画廊/相机上传到服务器(okhttp 问题)

如何从响应改造中检索 cookie,okhttp?