java.net.ProtocolException: неожиданный конец потока (реализация 'com.squareup.okhttp3: okhttp: 4.2.0') - PullRequest
1 голос
/ 23 сентября 2019

Я получаю это исключение: java.net.ProtocolException: неожиданное завершение потока. W / System.err: в okhttp3.internal.http1.Http1ExchangeCodec $ FixedLengthSource.read (Http1ExchangeCodec.kt: 392)

Я пытаюсь сделать SOAP-запрос.Я не всегда получаю это исключение.Иногда я получаю правильный ответ с веб-сервера.

Я изменил библиотеку синтаксического анализатора с SimpleXML на TikXML, но проблема все еще остается.В методе onResponse я ничего не делаю, но у меня была та же проблема.

Вот как я делаю вызов:

final Call<ResponseEnvelope> consumeWS = RetrofitGenerator
            .getConsultarTipoDeDocumentoApi()
            .getTiposDeDocumento(this.USERNAME_ENCRYPT, this.USERTOKEN_ENCRYPT,
                    this.MESSAGEID_ENCRYPT, "close", this.requestEnvelope);
    consumeWS.enqueue(new Callback<ResponseEnvelope>() {
        @Override
        public void onResponse(Call<ResponseEnvelope> call, Response<ResponseEnvelope> response) {
            ResponseEnvelope responseEnvelope = new ResponseEnvelope(response.body().getBody());
            if(responseEnvelope != null && response.isSuccessful())
                dataTipoDeDocumento.setValue(responseEnvelope.getBody().getMtrtipdoccResponse()
                        .getReturn().getLISTAREGISTROS().getLIST());
        }

        @Override
        public void onFailure(Call<ResponseEnvelope> call, Throwable t) {
            t.printStackTrace();
            dataTipoDeDocumento.setValue(null);

Вот мой RetrofitGenerator:

private static OkHttpClient.Builder okHttpClient = new OkHttpClient
        .Builder();

private static Retrofit.Builder retrofitBuilder =  new Retrofit.Builder()
        .addConverterFactory(SimpleXmlConverterFactory.create(serializer));

public RetrofitGenerator() {
}

public static <S> S createService(Class<S> serviceClass, String baseUrl) {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.level(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient client = okHttpClient
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)
            .addInterceptor(logging)
            .build();
    Retrofit retrofit = retrofitBuilder.baseUrl(baseUrl).client(client).build();
    return retrofit.create(serviceClass);
}

Вот проблема: (Отладка запроса)

override fun read(sink: Buffer, byteCount: Long): Long {
  require(byteCount >= 0L) { "byteCount < 0: $byteCount" }
  check(!closed) { "closed" }
  if (bytesRemaining == 0L) return -1
  val read = super.read(sink, minOf(bytesRemaining, byteCount))
  if (read == -1L) {
    realConnection!!.noNewExchanges() // The server didn't supply the promised content length.
    val e = ProtocolException("unexpected end of stream")
    responseBodyComplete()
    throw e
  }

Я заметил, что при сбое есть переменная с именем bytesRemaining, которая получает значение 1 (при неудаче всегда 1).Длина содержимого ответа всегда 933. Я не знаю, что происходит.

Вот мой выпускник:

apply plugin: 'com.android.application'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
    applicationId "com.app.multired"
    minSdkVersion 22
    targetSdkVersion 29
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

dataBinding {
    enabled = true
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'com.google.android.material:material:1.0.0'

implementation 'com.jakewharton:butterknife:10.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'

/* dagger dependency for DI*/
implementation "com.google.dagger:dagger:2.16"
annotationProcessor "com.google.dagger:dagger-compiler:2.16"
compileOnly 'javax.annotation:jsr250-api:1.0'
implementation 'javax.inject:javax.inject:1'

/*Retrofit lib*/
testImplementation 'com.squareup.okhttp3:mockwebserver:4.2.0'
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.0'
//implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.6.1'

/*RxJava lib*/
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation "io.reactivex.rxjava2:rxjava:2.1.8"
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

/* LiveData lib*/
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:runtime:1.1.1"
annotationProcessor "android.arch.lifecycle:compiler:1.1.1"
implementation 'androidx.recyclerview:recyclerview:1.0.0'

/* Biometric Authentication */
implementation 'androidx.biometric:biometric:1.0.0-alpha03'
implementation 'org.jetbrains:annotations:15.0'
}

Как я решил это:

public abstract class CallbackWithRetry<T> implements Callback<T> {
private static final String TAG = CallbackWithRetry.class.getSimpleName();

@Override
public void onFailure(Call<T> call, Throwable t){
    Log.e(TAG, t.getLocalizedMessage());
    retry(call);
}

private void retry(Call<T> call){
    call.clone().enqueue(this);
}
}

Отредактированный обратный вызов:

final Call<ConsultarTipoDeDocumentoResponseEnvelope> consumeWS = RetrofitGenerator
            .getConsultarTipoDeDocumentoApi()
            .getTiposDeDocumento(this.USERNAME_ENCRYPT, this.USERTOKEN_ENCRYPT,
                    this.MESSAGEID_ENCRYPT, this.consultarTipoDeDocumentoRequestEnvelope);
    consumeWS.enqueue(new CallbackWithRetry<ConsultarTipoDeDocumentoResponseEnvelope>() {
        @Override
        public void onResponse(Call<ConsultarTipoDeDocumentoResponseEnvelope> call, Response<ConsultarTipoDeDocumentoResponseEnvelope> response) {
            ConsultarTipoDeDocumentoResponseEnvelope consultarTipoDeDocumentoResponseEnvelope = new ConsultarTipoDeDocumentoResponseEnvelope(response.body().getBody());
            if(consultarTipoDeDocumentoResponseEnvelope != null && response.isSuccessful())
                dataTipoDeDocumento.setValue(consultarTipoDeDocumentoResponseEnvelope.getBody().getMtrtipdoccResponse()
                        .getReturn().getLISTAREGISTROS().getLIST());
        }

        @Override
        public void onFailure(Call<ConsultarTipoDeDocumentoResponseEnvelope> call, Throwable t) {
            super.onFailure(call, t);
        }
    });

Эта повторная попытка очень полезна, так как Retrofit2 включил переменную вызова в качестве параметра метода onFailure при выполнении вызова очереди.

1 Ответ

0 голосов
/ 24 сентября 2019

Я предполагаю, что сервер измерил длину ответа, используя String.length() (или аналогичный), но содержимое не было полностью ASCII, и поэтому закодированная длина в байтах была больше.

Вы можете подтвердить это, посмотревесли в прерванных ответах есть какие-либо не-ASCII символы.

...