首页 > 解决方案 > java.net.SocketTimeoutException:使用 Retrofit 和 OkHttp

问题描述

如果令牌随时过期,我创建了一个类来获取刷新令牌。在进行休息 API 调用之前始终调用此类。

这是我的课,

import android.content.Context;

import com.epic.ssb.data.TokenModel;
import com.epic.ssb.security.LogoutProcess;
import com.epic.ssb.security.TokenIdentifier;
import com.epic.ssb.util.Constant;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RefreshTokenInterceptor implements Interceptor {

    private Lock lock = new ReentrantLock();
    Context ctx;

    public RefreshTokenInterceptor(Context context){
        this.ctx = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        chain.withConnectTimeout( 45, TimeUnit.SECONDS);
        chain.withReadTimeout(45,TimeUnit.SECONDS);
        chain.withWriteTimeout(45,TimeUnit.SECONDS);
        Request request = chain.request();
        Response response = chain.proceed(request);

        if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
            if (lock.tryLock()) {
                try {
                    OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
                    httpClient.addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Interceptor.Chain chain) throws IOException {
                            Request original = chain.request();

                            Request request = original.newBuilder()
                                    .header("Content-Type", "application/json")
                                    .header("X-Requested-With", "XMLHttpRequest")
                                    .header("X-Authorization", "Bearer " + TokenIdentifier.getREFRESHTOKEN())
                                    .method(original.method(), original.body())
                                    .build();

                            return chain.proceed(request);
                        }
                    });

                    OkHttpClient client = httpClient.build();
                    Retrofit retrofit = new Retrofit.Builder()
                            .baseUrl(Constant.BASE_URL)
                            .addConverterFactory(GsonConverterFactory.create())
                            .client(client)
                            .build();

                    ApiService apiService = retrofit.create(ApiService.class);
                    Call<TokenModel> call = apiService.getTokenFromRefreshToken();

                    retrofit2.Response<TokenModel> response1 = call.execute();
                    if(response.body()!=null){
                        TokenModel tokenModel = response1.body();
                        TokenIdentifier.setTOKEN(tokenModel.getToken());
                    }

                    Request newRequest = recreateRequestWithNewAccessToken(chain);
                    return chain.proceed(newRequest);

                } catch (Exception ex) {
                    LogoutProcess.logout(ctx);
                    return response;
                } finally {
                    lock.unlock();
                }
            } else {
                lock.lock();
                lock.unlock();
                Request newRequest = recreateRequestWithNewAccessToken(chain);
                return chain.proceed(newRequest);
            }
        } else {
            return response;
        }
    }

    private Request recreateRequestWithNewAccessToken(Chain chain) {
        String freshAccessToken = TokenIdentifier.getTOKEN();
        return chain.request().newBuilder()
                .header("Content-Type", "application/json")
                .header("X-Requested-With", "XMLHttpRequest")
                .header("X-Authorization", "Bearer " + freshAccessToken)
                .method(chain.request().method(), chain.request().body())
                .build();
    }
}

注意:如果当前令牌已过期,则此类用于获取新令牌。

然后我随机得到下面的异常,因为上面的类被访问了,

W/System.err: java.net.SocketTimeoutException: timeout
W/System.err:     at okio.Okio$4.newTimeoutException(Okio.java:232)
        at okio.AsyncTimeout.exit(AsyncTimeout.java:285)
W/System.err:     at okio.AsyncTimeout$1.write(AsyncTimeout.java:184)
        at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:179)
        at okio.RealBufferedSink.write(RealBufferedSink.java:42)
        at okhttp3.internal.http1.Http1Codec$FixedLengthSink.write(Http1Codec.java:295)
        at okio.ForwardingSink.write(ForwardingSink.java:35)
        at okhttp3.internal.http.CallServerInterceptor$CountingSink.write(CallServerInterceptor.java:149)
W/System.err:     at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:179)
        at okio.RealBufferedSink.write(RealBufferedSink.java:48)
        at okhttp3.RequestBody$1.writeTo(RequestBody.java:73)
        at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:72)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
W/System.err:     at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
W/System.err:     at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
W/System.err:     at com.epic.ssb.network.RefreshTokenInterceptor.intercept(RefreshTokenInterceptor.java:39)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:147)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
W/System.err: Caused by: java.net.SocketException: Socket closed
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:124)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:161)
        at okio.Okio$1.write(Okio.java:79)
        at okio.AsyncTimeout$1.write(AsyncTimeout.java:180)
W/System.err:   ... 30 more

正如我在文章顶部提到的,这是用于安全的 Rest API 调用。不适用于未经授权的 Rest API 调用。

为什么这是随机发生的?我应该添加更多步骤吗?

标签: javaandroidretrofitokhttp3

解决方案


推荐阅读