今天想写一个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(); }
疑问解决了。