关于okhttp的ProtocolException("expected $contentLength bytes but received $newBytesReceived")
问题原因
OkHttp出现ProtocolException("expected $contentLength bytes but received $newBytesReceived")
的原因可能是服务器返回的Content-Length与实际接收到的数据长度不一致。这种不一致可能导致客户端接收数据时出现异常,从而抛出ProtocolException
异常。这种情况通常发生在服务器端未正确设置Content-Length的情况下,或者在传输过程中发生数据截断或篡改的情况下。
解决方案
当OkHttp出现ProtocolException("expected $contentLength bytes but received $newBytesReceived")异常时,这通常是由于服务器未正确设置Content-Length header导致内容长度不匹配所致。要解决这个问题,可以尝试以下几种方法: 1. 检查服务器设置:确保服务器正确设置Content-Length header,确保发送给客户端的数据长度正确。 2. 更新OkHttp版本:有时候这个问题可能是OkHttp的一个已知问题,在更新到最新版本的OkHttp时可能已经修复了这个问题。 3. 添加拦截器:可以添加一个拦截器来手动检查并修正响应的内容长度。拦截器的作用就是在请求和响应的处理过程中进行拦截、修改或添加信息。通过拦截器可以获取响应的内容长度并进行校验,如果不匹配可以进行相应的处理,比如截取正确长度的内容。 4. 处理异常:在发生ProtocolException异常时,可以尝试捕获这个异常并进行适当的处理,比如重试请求、重新连接等操作。 下面是一个使用拦截器来解决该问题的示例代码:
import okhttp3.Interceptor;
import okhttp3.Response;
import java.io.IOException;
Interceptor contentLengthInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (originalResponse.body() != null) {
long contentLength = originalResponse.body().contentLength();
String newBody = originalResponse.body().string();
if (contentLength != newBody.length()) {
newBody = newBody.substring(0, (int) contentLength); // 截取正确长度的内容
}
return originalResponse.newBuilder()
.body(ResponseBody.create(originalResponse.body().contentType(), newBody))
.build();
}
return originalResponse;
}
};
在上面的代码中,拦截器首先获取响应的内容长度,然后根据内容长度截取正确长度的内容,并将其重新构建一个新的Response对象。通过这种方式,可以避免ProtocolException异常的出现。
具体例子
当在使用OkHttp时出现ProtocolException("expected $contentLength bytes but received $newBytesReceived")的错误时,通常是因为服务器返回的响应体的长度与实际接收到的长度不一致引起的。这个问题通常发生在服务器发送了一个错误的Content-Length头部。 要正确处理这个问题,可以通过以下方式解决: 1. 验证响应体长度是否与Content-Length一致:在接收响应之前,可以先检查响应头中的Content-Length值,然后在读取响应体时确保读取的长度与Content-Length一致。 2. 使用Chunked编码:如果服务器使用Chunked编码传输数据,可以使用OkHttp的自动解析支持Chunked编码的方式来处理。在这种情况下,OkHttp会自动处理响应体长度的问题。 3. 处理流式数据:如果在接收响应时无法获取到Content-Length,可以考虑使用流式处理响应体,而不是依赖于 Content-Length 头的值。 以下是一个使用OkHttp正确处理ProtocolException的示例代码:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/data")
.build();
try (Response response = client.newCall(request).execute()) {
ResponseBody body = response.body();
if (body != null) {
// 检查Content-Length
long contentLength = body.contentLength();
if (contentLength != -1) {
BufferedSource source = body.source();
Buffer buffer = new Buffer();
source.read(buffer, contentLength);
// 处理响应数据
System.out.println(buffer.readUtf8());
} else {
// 没有Content-Length,处理流式数据
BufferedSource source = body.source();
Buffer buffer = new Buffer();
while (!source.exhausted()) {
source.read(buffer, 8192); // 每次读取8KB
}
// 处理响应数据
System.out.println(buffer.readUtf8());
}
}
} catch (IOException e) {
e.printStackTrace();
}
在上面的示例中,我们首先检查Content-Length,如果存在Content-Length,我们就读取指定长度的响应体;如果不存在Content-Length,我们就使用流式的方式来读取响应体。这样可以避免出现ProtocolException的问题,并正确处理响应数据。