• 原生Java做HTTP请求会强制断开服务器连接的错误(HttpURLConnection 异常关闭)


    今天想写一个Java http请求的工具包,为了方便性的考虑,使用原生Java。结果在写GET的时候就出了问题:

    package com.liushx.utils.Http;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    public class HttpRequester {
        private Integer connectTimeout = 30000;
        private Integer readTimeout = 30000;
    
        /**
         * 获取响应头,并把响应头转换成普通格式
         * @param result 输出的结果
         * @param source 响应头
         */
        public void parseHeader(Map<String, String> result, Map<String, List<String>> source) {
            Set<String> keys = source.keySet();
    
            for (String k: keys){
                List<String> value = source.get(k);
                result.put(k, value.get(0));
            }
        }
    
        /**
         * 配置请求头
         * @param header 请求头
         */
        public void setRequestHeader(Map<String, String> header, HttpURLConnection connection) {
            Set<String> keys = header.keySet();
            for (String key: keys){
                connection.setRequestProperty(key, header.get(key));
            }
        }
    
        public void GET(String url, Map<String, String> headers, HttpResult result){
            int code = 0;
            String text;
            InputStream stream = null;
            BufferedReader reader = null;
    
    
            Map<String, String> respHeaders = new HashMap<>();
    
            try {
                URL apiUrl = new URL(url);
    
                HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
                connection.setRequestMethod("GET");
                // 设置连接超时时间
                connection.setConnectTimeout(connectTimeout);
                // 设置响应超时时间
                connection.setReadTimeout(readTimeout);
    
                setRequestHeader(headers, connection);
                // 开始请求
                connection.connect();
                code = connection.getResponseCode();
                System.out.print(connection.getResponseMessage());
    
                if (code == 200){
                    stream = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(stream));
                    StringBuilder stringBuilder = new StringBuilder();
                    while ((text = reader.readLine()) != null){
                        stringBuilder.append(text);
                        stringBuilder.append("
    ");
                    }
                    result.setHttpResponseBody(stringBuilder.toString());
    
                    stream.close();
                    reader.close();
    connection.disconnect(); }
    else{ result.setErrorMessage(connection.getResponseMessage()); connection.disconnect(); } Map<String, List<String>> headersMap = connection.getHeaderFields(); parseHeader(respHeaders, headersMap); result.setResponseHeader(respHeaders); result.setCode(code); } catch (IOException e) { try{ if (stream != null){ stream.close(); } }catch (IOException ignored){} try{ if (reader != null){ reader.close(); } }catch (IOException ignored){} } } public Integer getReadTimeout() { return readTimeout; } public void setReadTimeout(Integer readTimeout) { this.readTimeout = readTimeout; } public Integer getConnectTimeout() { return connectTimeout; } public void setConnectTimeout(Integer connectTimeout) { this.connectTimeout = connectTimeout; } }

    另外用python的Django写了一个服务端,提供简单的接口用于测试

    在Java客户端得到数据,程序退出的时候,Django服务端报了个错误:

    ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
    

      

    可以看出来是socket异常断开导致的

    在网上找了一圈,发现一个大佬的一个说法:

    http://cn.voidcc.com/question/p-erxxoacl-by.html

    所以问题明确了,代码红色部分,关闭数据流和连接的顺序不对,如果想缓存socket的话,可以按照红色代码,最后关闭connection,这样会把连接进行缓存,从我这个报错来看,socket会保持连接。

    所以在我程序退出的时候,由于连接仍然存活,导致服务端在访问socket的时候出现了连接被强制断开的错误。

    正确的关闭顺序应该是这样:

                if (code == 200){
                    stream = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(stream));
                    StringBuilder stringBuilder = new StringBuilder();
                    while ((text = reader.readLine()) != null){
                        stringBuilder.append(text);
                        stringBuilder.append("
    ");
                    }
                    result.setHttpResponseBody(stringBuilder.toString());
    
                    // 关闭连接的顺序,如果connection.disconnect()放在stream和reader关闭后调用
                    // 那么将不会关闭客户端和服务器的socket连接,如此会导致脚本结束后,连接强制断开
                    // 所以,如果不需要缓存socket,那么应该首先断开socket连接
                    connection.disconnect();
                    stream.close();
                    reader.close();
                }else{
                    result.setErrorMessage(connection.getResponseMessage());
                    connection.disconnect();
                }

    疑问解决了。

  • 相关阅读:
    Consul 学习文章链接
    秒懂:tomcat 最大线程数 最大连接数
    使用 Spring Cloud Sleuth 实现链路监控 (转)
    (转)Java中OutOfMemoryError(内存溢出)的三种情况及解决办法
    @RequestMapping 用法
    Spring 常用的几种注解
    [转] Spring MVC 深入分析
    [转]session listener的配置和使用
    web.xml中 Log4jConfigListener配置
    [mysql] 手动备份数据
  • 原文地址:https://www.cnblogs.com/haiton/p/14668383.html
Copyright © 2020-2023  润新知