原生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();
            }

疑问解决了。

原文地址:https://www.cnblogs.com/haiton/p/14668383.html