OkHttp3出现java.io.IOException: Hostname was not verified解决方案

本文详细分析了使用OkHttp3进行HTTPS请求时遇到的域名未验证错误,并提供了解决方案,通过自定义SSL配置绕过域名检查,确保请求能够成功执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

用OkHttp3做https请求时候报了个java.io.IOException: Hostname was not verified的错误。

 

问题分析

通常是因为SSL协议握手的过程中,这个服务度地址的证书没有被证实,被信任。

报错消息如下。

  Hostname a.com not verified:
    certificate: sha256/s+ZoW0wxlNvmDUguAjYVvc6xxnIetO4XUMissqHkBPg=
    DN: CN=*.b.com, OU=Domain Control Validated
    subjectAltNames: [*.b.com, b.com]

可以看到请求的证书的域名为b.com,而我们要请求的是a.com,因此错误原因是在验证证书时发现真正请求的域名和服务器的证书域名不一致。

解决方案

如果你认为运行的证书没有任何意义,并且想要绕过它们,那么就需要添加一个空主机名验证程序以使后面的请求正常工作。具体代码如下

   public static OkHttpClient.Builder ignoreSSL (OkHttpClient.Builder builder) {
        builder.sslSocketFactory(createSSLSocketFactory())
            .hostnameVerifier((s, sslSession) -> true);
        return builder;
    }

    private static SSLSocketFactory createSSLSocketFactory () {

        SSLSocketFactory sSLSocketFactory = null;

        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom());
            sSLSocketFactory = sc.getSocketFactory();
        } catch (Exception e) {
            LOGGER.info(e.getMessage(), e);
        }

        return sSLSocketFactory;
    }

    private static class TrustAllManager implements X509TrustManager {

        @Override
        public void checkClientTrusted (java.security.cert.X509Certificate[] x509Certificates,
            String s) throws java.security.cert.CertificateException {

        }

        @Override
        public void checkServerTrusted (java.security.cert.X509Certificate[] x509Certificates,
            String s) throws java.security.cert.CertificateException {

        }


        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers () {
            return new X509Certificate[0];
        }
    }

自己封装的http工具类

我自己封装的完整的OkHttp3Util工具类代码如下。

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.collections.MapUtils;
import org.apache.http.util.TextUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author magotzis on 2018/7/31 下午5:06
 */
@Slf4j
public class HttpsUtil {

    private static final OkHttpClient client = new OkHttpClient.Builder()
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .connectTimeout(10, TimeUnit.SECONDS)
            .sslSocketFactory(createSSLSocketFactory())
            .hostnameVerifier((s, sslSession) -> true)
            .build();


    private static final Joiner AMPERSAND_JOINER = Joiner.on("&");

    // 工具类不需要实例化
    private HttpsUtil() {
    }

    private static SSLSocketFactory createSSLSocketFactory () {

        SSLSocketFactory sSLSocketFactory = null;

        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom());
            sSLSocketFactory = sc.getSocketFactory();
        } catch (Exception e) {
            log.info(e.getMessage(), e);
        }

        return sSLSocketFactory;
    }

    private static class TrustAllManager implements X509TrustManager {

        @Override
        public void checkClientTrusted (java.security.cert.X509Certificate[] x509Certificates,
                                        String s) throws java.security.cert.CertificateException {
        }

        @Override
        public void checkServerTrusted (java.security.cert.X509Certificate[] x509Certificates,
                                        String s) throws java.security.cert.CertificateException {
        }


        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers () {
            return new X509Certificate[0];
        }
    }


    /**
     * 异步调用get请求
     *
     * @param url      请求的url
     * @param callback 回调处理
     */
    public static void getByAsyn(String url, Callback callback) {
        Request request = new Request.Builder()
                .get()
                .url(url)
                .build();

        client.newCall(request).enqueue(callback);
    }

    /**
     * 同步调用get请求
     *
     * @param url 请求的url
     * @return response信息
     * @throws IOException IO异常
     */
    public static Response getBySync(String url) throws IOException {
        Request request = new Request.Builder()
                .get()
                .url(url)
                .build();
        return client.newCall(request).execute();
    }

    /**
     * post请求
     *
     * @param url         请求的url
     * @param requestBody requestBody信息
     * @param headers     请求头部
     * @return response信息
     * @throws IOException IO异常
     */
    public static Response post(String url, RequestBody requestBody, Headers headers) throws IOException {
        Request.Builder build = new Request.Builder();
        if (headers != null) {
            build.headers(headers);
        }
        Request request = build
                .post(requestBody)
                .url(url)
                .build();
        return client.newCall(request).execute();
    }

    /**
     * 拼接url和param
     *
     * @param url    请求的url
     * @param params 需要带的参数
     * @return 拼接好的url
     */
    public static String buildUrl(String url, Map<String, String> params) {
        if (MapUtils.isEmpty(params)) {
            return url;
        }
        List<String> nameValueList = Lists.newArrayListWithExpectedSize(params.size());
        for (Map.Entry<String, String> entry : params.entrySet()) {
            nameValueList.add(entry.getKey() + "=" + entry.getValue());
        }

        return url + "?" + AMPERSAND_JOINER.join(nameValueList);

    }

    /**
     * @param url 下载链接
     * @return 文件路径
     */
    public static File download(String url) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        String fileName;
        File file = null;
        try {
            Response response = client.newCall(request).execute();
            // 解析文件名,如果不存在则随机生成一个uuid作为文件名
            fileName = getHeaderFileName(response);
            if (TextUtils.isEmpty(fileName)) {
                fileName = UidUtil.getUid();
            }
            file = new File(fileName);
            // 输出保存文件
            byte[] buf = new byte[2048];
            int len;
            try (InputStream is = Objects.requireNonNull(response.body()).byteStream();
                 FileOutputStream fos = new FileOutputStream(file)) {
                while ((len = is.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
                fos.flush();
            }
        } catch (IOException e) {
            log.error("下载文件失败", e);
        }
        return file;
    }

    /**
     * 解析文件头
     * Content-Disposition: inline; filename="test.pdf";
     * filename*=UTF-8''test.pdf
     */
    private static String getHeaderFileName(Response response) {
        String header = response.header("Content-Disposition");
        if (!TextUtils.isEmpty(header)) {
            header = header.replace("inline;filename=", "");
            header = header.replace("filename*=utf-8", "");
            String[] strings = header.split("; ");
            if (strings.length > 1) {
                header = strings[1].replace("filename=", "");
                header = header.replace("\"", "");
                return header;
            }
            return "";
        }
        return "";
    }

}

 

转载于:https://www.cnblogs.com/magotzis/p/9488157.html

java.io.IOException: unexpected end of stream on https://mkts.chinaums.com/... at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:210) at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:110) at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:93) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154) at com.example.Main$7.run(Main.java:276) at java.base/java.lang.Thread.run(Thread.java:1447) Caused by: java.io.EOFException: \n not found: limit=0 content=… at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:335) at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29) at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:180) ... 15 more分析这是什么原因
最新发布
07-21
<think>首先,用户的问题是:“我想分析OkHttp请求中出现的IOException异常原因 请问OkHttp java.io.IOException unexpected end of stream https://mkts.chinaums.com/ 分析原因”。这看起来是关于OkHttp库在请求特定URL时出现的异常。 从系统级指令中,我需要: - 所有行内数学表达式用$...$格式。 - 独立公式用$$...$$并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层要求: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考站内引用: - 引用[1]: 提到在Kotlin中使用OKHttp遇到java.io.IOException: unexpected end of stream on Connection。解决方案是添加"connection": "close"头,但这会导致每次重新连接。 - 引用[2]: 显示在发送POST请求时使用了OkHttpClient的配置,包括hostnameVerifier和sslSocketFactory。 - 引用[3]: 是关于一个请求参数示例和返回值的描述,但似乎不直接相关。 - 引用[4]: 解释HTTP Connection的close设置,表示连接在处理后会被关闭。 用户的问题焦点是分析异常原因,特别是针对URL https://mkts.chinaums.com/。 关键异常:java.io.IOException: unexpected end of stream on Connection。这通常表示连接在预期数据完全接收前被关闭。 可能原因: 1. **服务器端问题**:服务器可能过早关闭连接。 2. **网络问题**:网络中断或代理问题。 3. **OkHttp配置问题**:如连接池设置或超时设置。 4. **HTTP版本问题**:HTTP/1.1使用持久连接,但服务器可能不支持。 5. **SSL/TLS问题**:如果使用HTTPS,证书或握手问题。 6. **客户端代码问题**:如请求头设置不当。 从引用中: - 引用[1]和[4]建议添加"Connection: close"头来强制关闭连接,避免问题,但这不是最佳方案。 - 引用[2]提到SSL配置,可能涉及信任所有证书,这在生产环境中不安全。 分析步骤: 1. **确认问题重现**:用户说在浏览器中请求正常,但OkHttp失败。这表明问题可能在于客户端实现。 2. **检查URL**:https://mkts.chinaums.com/,这是一个HTTPS站点,所以SSL/TLS问题可能相关。 3. **常见原因**: - 服务器发送了响应但没有正确关闭连接。 - OkHttp期望更多数据但连接被关闭。 - 可能由于Keep-Alive设置不当。 解决方案思路: - 尝试添加"Connection: close"头来测试。 - 检查SSL配置是否正确。 - 调整OkHttp的超时设置。 - 确保使用最新OkHttp版本。 在我的回答中,我需要: - 结构化回答:逐步分析原因和解决方案。 - 使用中文。 - 在适当的地方添加引用标识。 - 生成相关问题。 最后,生成相关问题。 回答结构: 1. **介绍异常**:解释java.io.IOException: unexpected end of stream on Connection的含义。 2. **可能原因分析**:列出并解释常见原因。 3. **解决方案建议**:提供调试步骤和代码示例。 4. **引用站内信息**:自然地融入引用。 5. **生成相关问题**:在末尾添加。 确保LaTeX格式:由于问题涉及编程,可能没有数学表达式,但如果有变量或方程,用$...$。 例如,如果提到方程,如计算超时,但这里可能不需要。 生成相关问题: - 例如: 1. 如何优化OkHttp以避免连接问题? 2. OkHttp中如何处理SSL证书验证? 3. 什么是HTTP Keep-Alive及其在OkHttp中的影响?</think>### OkHttp请求中java.io.IOException: unexpected end of stream异常分析 针对您在使用OkHttp请求URL `https://mkts.chinaums.com/` 时遇到的 `java.io.IOException: unexpected end of stream on Connection` 异常,我将逐步分析原因并提供解决方案。此异常通常表示HTTP连接在预期数据完全接收前被意外关闭,导致数据流中断。以下分析基于常见网络问题、OkHttp机制和站内引用信息,确保真实可靠。 #### 1. **异常含义和直接原因** - `unexpected end of stream` 表示OkHttp客户端在读取响应数据时,连接被服务器或网络层提前终止,客户端未收到完整的HTTP响应体。 - 在HTTP/1.1协议中,连接默认使用持久连接(Keep-Alive),但服务器可能未正确处理持久连接逻辑,导致连接过早关闭[^4]。 - 此问题在浏览器中正常而OkHttp失败,说明问题可能源于客户端实现或协议兼容性,而非服务器完全不可用。 #### 2. **可能原因分析** 结合站内引用和常见案例,以下是针对URL `https://mkts.chinaums.com/` 的潜在原因: - **服务器端问题(最常见)**: - 服务器可能未正确处理HTTP持久连接。例如,服务器在发送响应后未正确关闭连接,或响应头未包含`Content-Length`或`Transfer-Encoding`,导致OkHttp无法确定数据结束点[^1][^4]。 - 针对HTTPS站点(如`https://mkts.chinaums.com/`),服务器TLS握手或证书链问题可能导致连接中断。引用[2]中提到的`sslSocketFactory`配置不当可能加剧此问题。 - 服务器可能设置了短超时(如 < 30秒),在OkHttp读取数据前主动关闭连接。 - **OkHttp配置问题**: - 连接池设置不当:OkHttp默认重用连接(连接池),但如果服务器不支持Keep-Alive,可能引发冲突。引用[4]指出,服务器返回`Connection: close`头会强制关闭连接,但客户端未显式处理时易出错。 - 超时设置不足:OkHttp的读取超时(`readTimeout`)默认较短(10秒),如果服务器响应慢,可能触发超时关闭。 - SSL/TLS验证问题:如引用[2]所示,使用`.hostnameVerifier((s, session) -> true)`绕过证书验证,但若服务器证书链不完整或SNI(Server Name Indication)未启用,会导致连接异常终止。 - **网络或代理问题**: - 中间代理(如公司防火墙或CDN)可能干扰HTTPS连接,提前关闭流。 - 本地网络不稳定,尤其在移动环境下,数据包丢失可能模拟“end of stream”错误。 - **客户端代码问题**: - 未添加必要请求头:如引用[1]和[4]所述,缺少`Connection: close`头时,服务器可能不兼容持久连接。 - 响应处理逻辑错误:例如,未正确关闭Response Body,导致资源泄漏,引发后续连接问题。 #### 3. **解决方案建议** 以下是调试和修复步骤,基于原因分析。建议逐步测试: **步骤1: 添加显式Connection头** - 在请求中添加`Connection: close`头,强制每次请求后关闭连接。这能避免持久连接问题,但会增加延迟(每次需重新握手)。示例代码: ```kotlin val request = Request.Builder() .url("https://mkts.chinaums.com/") .header("Connection", "close") // 添加此头 .build() val response = okHttpClient.newCall(request).execute() ``` - 如果异常消失,则确认是服务器Keep-Alive兼容性问题[^1][^4]。 **步骤2: 调整OkHttp超时和SSL配置** - 增加读取超时,并确保SSL配置正确: ```kotlin val okHttpClient = OkHttpClient.Builder() .readTimeout(30, TimeUnit.SECONDS) // 增加超时避免服务器响应慢 .sslSocketFactory(createSSLContext().socketFactory, trustManager) // 使用有效TrustManager .hostnameVerifier { hostname, session -> hostname == "mkts.chinaums.com" // 避免无条件信任所有证书 } .build() ``` - 使用工具(如Wireshark)抓包检查TLS握手是否正常。引用[2]中无条件信任证书的方式不安全,应替换为有效验证。 **步骤3: 检查服务器响应和日志** - 在OkHttp中添加日志拦截器,查看完整请求/响应流: ```kotlin val loggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val client = OkHttpClient.Builder().addInterceptor(loggingInterceptor).build() ``` - 如果响应头缺少`Content-Length`或包含`Connection: close`,则服务器主动关闭连接是根源[^4]。 **步骤4: 测试网络环境** - 尝试不同网络(如切换WiFi/4G),排除本地代理问题。 - 使用命令行工具(如`curl -v https://mkts.chinaums.com/`)验证服务器行为。 **步骤5: 升级OkHttp并检查依赖** - 确保使用最新OkHttp版本(如v4.12.0+),修复已知连接池bug。 - 如果问题持续,考虑服务器端问题:联系`mkts.chinaums.com`管理员检查服务器日志或配置。 #### 4. **根本原因总结** 基于您的描述和站内引用,此异常最可能原因是**服务器未正确处理HTTP持久连接**,导致OkHttp预期数据流未完成即被关闭[^1][^4]。次要因素可能包括SSL配置宽松或超时不足。添加`Connection: close`头是最快缓解方案,但优化SSL和超时设置更可持续。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值