【问题标题】:java.net.ProtocolException: unexpected end of stream (implementation 'com.squareup.okhttp3:okhttp:4.2.0')java.net.ProtocolException:流意外结束(实现 'com.squareup.okhttp3:okhttp:4.2.0')
【发布时间】:2020-01-23 18:51:41
【问题描述】:

我遇到了这个异常: 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 方法的参数。

【问题讨论】:

    标签: okhttp


    【解决方案1】:

    我的猜测是服务器使用String.length()(或类似的)测量响应长度,但内容并非全是 ASCII,因此以字节为单位的编码长度更大。

    您可以通过查看损坏的响应中是否有任何非 ASCII 字符来确认这一点。

    【讨论】:

    • 您可能是对的,因为网络服务的答案是加密的。我通过 CallbackRetry 解决了它。如您所见,我编辑了我的问题,展示了我是如何解决问题的。
    猜你喜欢
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 2022-08-24
    • 2018-05-05
    • 2019-01-20
    相关资源
    最近更新 更多