发布大型正文时,okHttp 总是超时

Posted

技术标签:

【中文标题】发布大型正文时,okHttp 总是超时【英文标题】:okHttp always timeout when POST a large body 【发布时间】:2021-10-16 08:08:47 【问题描述】:

我使用 okHttp 做一个 POST 请求,当字段 String.length 小于 500 左右时它可以工作。

这样的代码,okHttp的常规使用方式:

Request.Builder builder = new Request.Builder();
builder.url(getHostname() + getUrl());
builder.post(new FormBody.Builder.add("key", "the large string").build())
Call call = mOkHttpClient.newCall(builder.build());
call.enqueue(new Callback() 
    @Override
    public void onFailure(Call call, IOException e) 
        e.printStackTrace();
    

    @Override
    public void onResponse(Call call, Response response) throws IOException 
        String output = response.body().string();
        Log.d("okHttp response", output);
    
);

但是当这个参数字段长度 > 500,比如 1k,不确定确切的值时,它会抛出 java.net.SocketTimeoutException: timeout:

    java.net.SocketTimeoutException: timeout
    at okio.SocketAsyncTimeout.newTimeoutException(JvmOkio.kt:143)
    at okio.AsyncTimeout.access$newTimeoutException(AsyncTimeout.kt:162)
    at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:335)
    at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:427)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:320)
    at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:178)
    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
    at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
    at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
    at java.lang.Thread.run(Thread.java:764)
    Caused by: java.net.SocketException: Socket closed
    at java.net.SocketInputStream.read(SocketInputStream.java:203)
    at java.net.SocketInputStream.read(SocketInputStream.java:139)
    at okio.InputStreamSource.read(JvmOkio.kt:90)
    at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
    ... 20 more

我是否设置超时:

    int TIMEOUT = 60;
    mOkHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(TIMEOUT,TimeUnit.SECONDS)
                    .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
                    .build();

它总是在我设置的 TIMEOUT 之后抛出 SocketTimeoutException。就好像它甚至不发送数据只是等待超时。

我试过了:

1.扩展的TIMEOUT设置值,但它只是保持发布并抛出timeoutException直到时间结束。

2.在postman中测试使用同样的大数据,上传耗时1s。字段长度不算长,8k左右。

3.现在将okhttp版本从3.12.0更新到最新的4.9.1。

但没有解决这个问题。

我继续测试程序,发现了一些新的东西:

此问题仅出现在 android Pad 上。当我用我的手机测试时,相同的数据不会超时。

1.我在华为 P30 pro(android 10) 上测试过,它可以在三星 Galaxy Tab active pro(android11) 和小米 PAD4(android8.1) 上运行,我不确定这是否只发生在 pad 上,或者这只是一个巧合。

2.我用我的三星平板进行了测试,并与 Charles 建立了代理。当我使用代理访问网络并想要捕获内容数据时,该程序可以正常工作。如果代理被取消,问题又出现了。

【问题讨论】:

记录getHostname() + getUrl() 并查看完整的url。你在邮递员中使用的体型是什么? url 是对的,POSTMAN 使用 form-data 或 x-www-form-urlencoded 都可以。当“key”字段长度小于500时代码可以在1s内得到响应,但是当字段长度刚刚增加到750时继续发送,无论我如何扩展超时设置,它只是继续发送utils时间结束。 是本地服务器吗? 不是本地服务器,因为它是远程服务器。我可以在 android 上的 post 参数长度小于 500 字节时得到响应,也可以通过 postman 得到响应,无论参数长度有多长。当我在 android 上更改此参数的长度时无法响应。这是唯一的区别。 也许可以针对httpbin.org/post 之类的测试站点进行测试,看看这是一般问题,还是您的服务器正在做的事情。 【参考方案1】:

问题已解决。是网络配置问题。经过一些对比测试,我尝试了热点,或者蜂窝网络,或者换了另一个WiFi。这个 POST API 可以成功。奇怪的是,在同一个WiFi下,手机和PAD还是有区别的。不过还是不知道是路由器的wifi配置的问题还是pad的wifi配置的问题。

【讨论】:

以上是关于发布大型正文时,okHttp 总是超时的主要内容,如果未能解决你的问题,请参考以下文章

使用Retrofit(Okhttp)时如何从android studio中的log cat复制请求正文,标头(不带标签)?

当使用 okhttp 发送 post 请求时,服务器接收到的请求正文为空。可能是啥问题?

在 OkHTTP 中处理超时

OkHttp/Retrofit 默认超时

OkHttp如何记录请求正文

okhttp请求超时无效问题