Vis*_*ram 7 java android tcp keep-alive httpurlconnection
从我的Android应用程序中,我想将数据发布到服务器并获取响应,处理它然后发回并获得另一个请求.由于它是持续的沟通,直到没有更多的响应过程,我更喜欢HttpURLConnection与http.keepAlive = true.
我尝试重用套接字是成功的,但我面临的问题是:
TIME_WAIT状态.我不希望我的服务器进入该状态,所以我更喜欢我的客户端启动终止.但不幸的是,我找不到合适的方法来做到这一点HttpURLConnectionkeepalivetimeout在服务器发送时启动Close
,但是当服务器发送时FIN,客户端只响应ACK,因为FIN_WAIT_2在服务器和CLOSE_WAIT代理中保持了连接
.
源代码:
private HttpStatus communicateWithMDMServer(String httpUrl, String dataToSend, boolean keepAlive) {
HttpStatus status = new HttpStatus(HTTP_STATUS_FAILURE);
try {
initializeConnection(httpUrl,keepAlive);
postDataToConnection(connection, dataToSend);
status = readDataFromConnection(connection);
} catch (MalformedURLException e) {
MDMLogger.error("Failed to send data to server as the URL provided is not valid "+ e.getMessage()+"\n");
e.printStackTrace();
} catch (IOException e) {
MDMLogger.error("Failed to send the status to the server : IOException "+ e.getMessage()+"\n"+e.getStackTrace());
e.printStackTrace();
readErrorStreamAndPrint(connection);
}
connection.disconnect();
return status;
}
/**
* API to close connection, calling this will not force the connection to shutdown
* this will work based on the Connection header set.
* @param connection
*/
public void closeConnection(){
if(connection != null){
connection.disconnect();
}
}
/**
* Used to initialize the HttpURLConnection for the given url
* All properties required for connection are preset here
* Connection Time Out : 20 Seconds
* Connection Type : keep alive
* Content Type : application/json;charset=UTF-8
* And also All certificates will be evaluated as Valid.[ TODO will be removed soon]
* @param httpUrl
* @return
* @throws MalformedURLException
* @throws IOException
*/
private void initializeConnection(String httpUrl, boolean keepAlive) throws MalformedURLException, IOException{
URL url = new URL(httpUrl);
connection = (HttpsURLConnection) url.openConnection();
connection.setConnectTimeout(20000);
connection.setReadTimeout(20000);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod("POST"); //NO I18N
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); //NO I18N
connection.setRequestProperty("Connection", "Keep-Alive"); //NO I18N
}
/**
* API to post data to given connection
* call to this API will close the @OutputStream
* @param connection
* @param data
* @throws IOException
*/
private void postDataToConnection(URLConnection connection , String data) throws IOException{
OutputStream outStream = connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream));
writer.write(data);
writer.flush();
writer.close();
outStream.close();
}
/**
* API to read error stream and log
* @param connection
*/
private void readErrorStreamAndPrint(URLConnection connection){
try{
InputStream inStream = ((HttpURLConnection) connection).getErrorStream();
String responseData = "";
String line;
BufferedReader br=new BufferedReader(new InputStreamReader(inStream));
while ((line=br.readLine()) != null) {
responseData+=line;
}
MDMLogger.error("ErrorStream Says "+responseData);
} catch (IOException ioe) {
MDMLogger.debug("Exception on reading ErrorStream");
}
}
/**
* API to read data from given connection and return {@code com.manageengine.mdm.framework.communication.HttpStatus}
* call to this API will close the @InputStream
* @param connection
* @return
* @throws IOException
*/
private HttpStatus readDataFromConnection(URLConnection connection) throws IOException{
HttpStatus status = new HttpStatus(HTTP_STATUS_FAILURE);
int responseCode=((HttpURLConnection) connection).getResponseCode();
MDMLogger.info("Response Code: "+responseCode);
InputStream inStream = connection.getInputStream();
MDMLogger.info("Response Header: "+connection.getHeaderFields());
String responseData = "";
if (responseCode == HttpURLConnection.HTTP_OK) {
responseData = readStreamAsString(inStream);
status.setStatus(HTTP_STATUS_SUCCESS);
status.setUrlDataBuffer(responseData);
MDMLogger.info("communicateWithMDMServer : Response is \n" + status.getUrlDataBuffer());
MDMLogger.info("Successfully send the data to server and received success response ");
}
else {
status.setStatus(HTTP_STATUS_FAILURE);
MDMLogger.error("Data sent successfully but failed to get the response and the response code is : " + responseCode);
}
inStream.close();
return status;
}
/**
* Read the InputStream to String until EOF
* Call to this API will not close @InputStream
* @param inStream
* @return
* @throws IOException
*/
private String readStreamAsString(InputStream inStream) throws IOException{
StringBuilder responseData = new StringBuilder();
String line;
BufferedReader br=new BufferedReader(new InputStreamReader(inStream));
while ((line=br.readLine()) != null) {
responseData.append(line);
}
return responseData.toString();
}
Run Code Online (Sandbox Code Playgroud)
有人可以帮忙吗?
当您使用http.keepAlive = true连接时,开始在连接池中回收并保持打开状态.即使服务器关闭了它仍在监听的连接,客户端仍然认为它可以发送数据.毕竟,服务器FIN只说服务器不会发送更多数据.
由于连接池的内部逻辑无法访问,因此您几乎无法控制.然而,当你使用https,你有一个开放的窗口,较低层,并且可以访问所创建的Socket通过HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory).
您可以为默认工厂(SSLSocketFactory.getDefault())创建一个允许关闭的包装器Socket.下面的简化:
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
private Socket last;
public void closeLastSocket() {
if (last != null) {
last.close();
}
}
public Socket createSocket() throws IOException {
return this.last = factory.createSocket();
}
...
}
Run Code Online (Sandbox Code Playgroud)
当您关闭底层套接字时,该连接不应该有资格进行回收,并将被丢弃.因此,如果您这样做closeLastSocket,disconnect并且连接甚至不应该转到池中,如果您执行相反的连接,则只有在创建新连接时才会丢弃该连接.