Java.net.SocketTimeoutException: 超时。

73

使用 OkHttp 库时,应用程序遇到以下 SocketTimeoutException 问题。如果请求大小较小,则它运行正常(小于1MB)。即使我的套接字超时(readTimeout)值要高得多,我也在10秒内收到此异常。对于一个请求(大小为1.8MB),它一直失败。当我使用 HttpUrlConnection 执行请求时,它可以正常工作。可能的失败原因是什么?

   03-29 12:16:38.997 32066-4018/com.mobile W/System.err: java.net.SocketTimeoutException: timeout
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.Okio$3.newTimeoutException(Okio.java:207)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.AsyncTimeout.exit(AsyncTimeout.java:261)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.AsyncTimeout$1.write(AsyncTimeout.java:158)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:176)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.RealBufferedSink.write(RealBufferedSink.java:46)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.internal.http.Http1xStream$FixedLengthSink.write(Http1xStream.java:286)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:176)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okio.RealBufferedSink.write(RealBufferedSink.java:96)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.RequestBody$2.writeTo(RequestBody.java:96)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:704)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.internal.http.HttpEngine.readResponse(HttpEngine.java:563)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.RealCall.getResponse(RealCall.java:241)
    03-29 12:16:38.997 32066-4018/com.mobile W/System.err:     at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:198)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:160)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at okhttp3.RealCall.execute(RealCall.java:57)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at com.mobizio.api.BaseApi.sendOkHttpRequest(BaseApi.java:81)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at com.mobizio.api.BaseApi.doInBackground(BaseApi.java:45)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at com.mobizio.api.BaseApi.doInBackground(BaseApi.java:30)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:292)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at java.lang.Thread.run(Thread.java:818)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err: Caused by: java.net.SocketException: socket is closed
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:759)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at okio.Okio$1.write(Okio.java:80)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:     at okio.AsyncTimeout$1.write(AsyncTimeout.java:155)
    03-29 12:16:38.998 32066-4018/com.mobile W/System.err:  ... 20 more

给你的客户端一个更大的读取超时值。似乎当从套接字读取流时,会发生超时。 - Nikola Despotoski
1
@NikolaDespotoski 我已经将套接字超时设置为15分钟,但我仍然遇到了这个问题。 - Vivek
10个回答

70

对于OkHttp 3,OkHttp的默认超时时间为10秒。您可以将超时时间增加到30秒。

OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(30, TimeUnit.SECONDS); // connect timeout
client.setReadTimeout(30, TimeUnit.SECONDS);    // socket timeout

2
我已经将连接超时值设置为30秒,套接字超时值设置为15分钟。但仍然遇到相同的问题,应用程序在10秒内出现异常。 - Vivek
@Vivek,你找到解决方案了吗? - DBragion
2
根据Square提供的文档https://github.com/square/okhttp/wiki/Recipes,建议在请求大小超过1MiB时不要使用此post string方法。您可以回退到HttpUrlConnection方法,或者按照文档中描述的实现post streaming。 - Vivek
2
@Vivek - 你找到解决方案了吗?我也遇到了类似的问题。 - Alyoshak
2
请勿实现后续流或使用HttpURLConnection。 - Vivek
1
我已经设置为5分钟,但仍然遇到了这个问题。 - Bhavesh Hirpara

59

我通过增加writeTimeout()来解决了那个问题。

尝试一下:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(5, TimeUnit.MINUTES) // connect timeout
.writeTimeout(5, TimeUnit.MINUTES) // write timeout
.readTimeout(5, TimeUnit.MINUTES); // read timeout

okHttpClient = builder.build();

1
设置 writeTimeout 也解决了问题,谢谢 :) - Yusuf Çağlar
2
如果我们想要上传文件,就必须像上面提到的那样更改默认的writeTimeout。 - Nandha Kumar

22
这解决了我的问题。
OkHttpClient innerClient = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.MINUTES) // connect timeout
            .writeTimeout(5, TimeUnit.MINUTES) // write timeout
            .readTimeout(5, TimeUnit.MINUTES) // read timeout
            .build();

2
这与其他人评论的有何不同? - Zun
4
没有像前两个(setConnectTimeout和setReadTimeout)那样的设置器,我认为我的更有条理 :) 同时,没有一个被标记为已解决 ;) - ronin_99
1
挽救了我的一天。我差点在网上胡闹。 - 4xMafole
1
我认为这是目前最漂亮的解决方案 - IonicMan

8

你需要明白,仅仅添加这个并不能解决你的问题:

OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)

如果您正在使用 Kotlin + Retrofit + Coroutines,则可以像下面这样使用 try 和 catch 处理网络操作:
viewModelScope.launch(Dispatchers.IO) {
        try {
            val userListResponseModel = apiEndPointsInterface.usersList()
            returnusersList(userListResponseModel)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

Exceptionkotlin 类型,而不是 java.lang 类型。

这将处理每一个异常,例如:

  1. HttpException
  2. SocketTimeoutException
  3. FATAL EXCEPTION: DefaultDispatcher 等等

这是我的 usersList() 函数:

@GET(AppConstants.APIEndPoints.HOME_CONTENT)
suspend fun usersList(): UserListResponseModel

7

使用此代码适用于Kotlin。

 val client1 = OkHttpClient.Builder()
                .connectTimeout(2, TimeUnit.MINUTES)
                .writeTimeout(2, TimeUnit.MINUTES) // write timeout
                .readTimeout(2, TimeUnit.MINUTES) // read timeout
                .addInterceptor(
                    BasicAuthInterceptor(
                        AmvaccAppConstants.AUTHENTICATE_USER_NAME, AmvaccAppConstants.AUTHENTICATE_USER_PASSWORD
                    )
                )
                .addInterceptor(interceptor)
                .build()

2
If you are using Retrofit and Kotlin then use following code:
    var BASE_URL:String="Your URL"
    val clientSetup = OkHttpClient.Builder()
        .connectTimeout(1, TimeUnit.MINUTES)
        .writeTimeout(1, TimeUnit.MINUTES) // write timeout
        .readTimeout(1, TimeUnit.MINUTES) // read timeout
        .build()

    val getClientApi: ApiInterface
        get() {
            var retrofit: Retrofit =  Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(clientSetup)
                .build()
        }

这个对我有帮助,但是你能告诉我默认的超时时间是多少吗?还有没有其他更好的方法来避免这个问题? - Gulab Sagevadiya

0
在我的情况下,将ping间隔添加到OkHttp中,对于减少应用程序中的SocketTimeoutExceptions数量非常有帮助。
OkHttpClient.Builder()
        .pingInterval(3, SECONDS)
        .build()

但我不确定这些超时是否与请求大小有关。我也在报告给OkHttp库的GitHub问题中提到了这个问题。


0
增加超时时间就像贴上创可贴一样,它并不能解决问题。出现这种情况肯定有原因,如果你正在使用OkHTTP,你会发现这一行代码。
 override fun intercept(chain: Interceptor.Chain): Response {
       var request = chain.request()
       ...
}

执行okHttp库内部的proceed方法,该方法会抛出一个IOException异常。
interface Chain {
    fun request(): Request

    @Throws(IOException::class)
    fun proceed(request: Request): Response

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    fun connection(): Connection?

    fun call(): Call

 ...

为了解决这个问题,你必须在一个try catch函数中运行它,并返回一个模拟的错误响应代码和消息,这样负责处理的团队就可以轻松地访问错误并进行调试,而不会导致用户应用崩溃。
override fun intercept(chain: Interceptor.Chain): Response {
        return try {
            
         var request = chain.request()

        } catch(e: IOException) {
          
            Response.Builder()
            .code(500)
            .message("Request failed with: $e.message")
            .build()
}

这样做可以捕获任何IOException,其中之一是SocketTiemoutException,但将来可能是任何其他异常。

-1

遇到一个运行时异常

runCatching {
       service
}
 .map {
       if (it.isSuccessful && it.body() != null) {
           Success(Unit)
       } else {
           Error(Failure.AuthError.error(it.errorBody()))
       }
}
 .getOrElse {
       Error(Failure.Throwable(it))
}

-3

检查您的URL是否正确。此错误可能是由于URL行不正确造成的。


如果服务器宕机怎么办?那么它的行为就像是错误的URL一样,因为响应不会从服务器到达。因此,设置正确的URL并不是解决方案。 - Kishan Solanki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接