从我的
Android应用程序中,我想将数据发布到服务器并获取响应,处理它然后发回并获得另一个请求.由于它是持续通信直到没有更多响应进程,我更喜欢使用http.keepAlive = true的HttpURLConnection.
我尝试重用套接字是成功的,但我遇到的问题是:
>我试图从客户端(Android应用程序)启动关闭,因为如果
终止从服务器开始,然后服务器进入
TIME_WAIT状态.我不希望我的服务器进入该状态,所以我更喜欢我的客户端启动终止.但不幸的是
我发现没有合适的方法来使用HttpURLConnection
>经过几个小时的搜索,我放弃了做上述尝试
从基于服务器的启动关闭开始
keepalivetimeout,但是当服务器发送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(); }
有人可以帮忙吗?
解决方法
当您使用http.keepAlive = true时,连接开始在连接池中循环使用,并保持打开状态.即使服务器关闭了它仍在监听的连接,客户端仍然认为它可以发送数据.毕竟,服务器的FIN只表示服务器不会发送更多数据.
由于连接池的内部逻辑无法访问,因此您几乎无法控制.然而,当您使用https时,您有一个打开较低层的窗口,可以通过HttpsURLConnection.setSSLSocketFactory(SSLSocketFactory)访问创建的Socket.
您可以为默认工厂(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(); } ... }
当您关闭底层套接字时,该连接不应该有资格进行回收,并将被丢弃.因此,如果您执行closeLastSocket并断开连接,则连接甚至不应该转到池中,如果您执行其他方式,则只有在创建新连接时才会丢弃连接.