使用HttpClient API打开2个插槽的HTTPS POST

问题描述:

经过大量的R & D和Google搜索,无法解决我的问题。使用HttpClient API打开2个插槽的HTTPS POST

环境设置

Web服务器(Tomcat的6.0.20) - >代理服务器(Windows Server 2007) - > Thirdy栏目主持人

我们有应用程序,这确实在线支付交易,本次交易完成后,我们希望将交易状态发送给第三方服务器。因此,从我们的Web服务器向第三方服务器发布数据时,会在代理服务器上为一个事务打开2个套接字,但是当我们在Web服务器上检查时,它只创建了一个套接字。 SO为什么2代理服务器上的套接字。

下面是我的示例代码

import javax.net.ssl.*; 
import javax.net.SocketFactory; 
import java.net.*; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 
import java.security.MessageDigest; 
import java.util.Hashtable; 
import java.math.BigInteger; 
import org.apache.commons.httpclient.*; 
import org.apache.commons.httpclient.methods.PostMethod; 
import org.apache.commons.httpclient.protocol.*; 


public class HTTPPostDemo { 

    private String privateKey; 
    private String host; 
    private int port; 
    private String userName; 
    private Header[] headers = null; 

    public class MySSLSocketFactory implements SecureProtocolSocketFactory { 

     private TrustManager[] getTrustManager() { 
      TrustManager[] trustAllCerts = new TrustManager[]{ 
       new X509TrustManager() { 
        public java.security.cert.X509Certificate[] getAcceptedIssuers() { 
         return null; 
        } 
        public void checkClientTrusted(
          java.security.cert.X509Certificate[] certs, String authType) { 
        } 
        public void checkServerTrusted(
          java.security.cert.X509Certificate[] certs, String authType) { 
        } 
       } 
      }; 
      return trustAllCerts; 
     } 

     public Socket createSocket(String host, int port) throws IOException, UnknownHostException { 

      TrustManager[] trustAllCerts = getTrustManager(); 
      try { 
       SSLContext sc = SSLContext.getInstance("SSL"); 
       sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
       HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 
       SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 
       return socketFactory.createSocket(host, port); 
      } catch (Exception ex) { 
       throw new UnknownHostException("Problems to connect " + host + ex.toString()); 
      } 
     } 

     public Socket createSocket(Socket socket, String host, int port, boolean flag) throws IOException, UnknownHostException { 
      TrustManager[] trustAllCerts = getTrustManager(); 
      try { 

       SSLContext sc = SSLContext.getInstance("SSL"); 
       sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
       HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 
       SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 
       return socketFactory.createSocket(host, port); 
      } catch (Exception ex) { 
       throw new UnknownHostException("Problems to connect " + host + ex.toString()); 
      } 

     } 

     public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { 

      TrustManager[] trustAllCerts = getTrustManager(); 
      try { 
       SSLContext sc = SSLContext.getInstance("SSL"); 
       sc.init(null, trustAllCerts, new java.security.SecureRandom()); 
       HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 
       SocketFactory socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); 
       return socketFactory.createSocket(host, port, clientHost, clientPort); 
      } catch (Exception ex) { 
       throw new UnknownHostException("Problems to connect " + host + ex.toString()); 
      } 

     } 
    } 

    public SslClient(String host, int port, String userName, String privateKey) { 
     this.host = host; 
     this.port = port; 
     this.userName = userName; 
     this.privateKey = privateKey; 
    } 

    protected String md5Sum(String str) { 
     String sum = new String(); 
     try { 
      MessageDigest md5 = MessageDigest.getInstance("MD5"); 
      sum = String.format("%032x", new BigInteger(1, md5.digest(str.getBytes()))); 
     } catch (Exception ex) { 
     } 
     return sum; 

    } 

    public String getSignature(String xml) { 
     return md5Sum(md5Sum(xml + privateKey) + privateKey); 
    } 

    public String sendRequest(String xml) throws Exception { 

     HttpClient client = new HttpClient(); 
     client.setConnectionTimeout(60000); 
     client.setTimeout(60000); 
     String response = new String(); 
     String portStr = String.valueOf(port); 
     Protocol.registerProtocol("https", new Protocol("https", new MySSLSocketFactory(), port)); 
     String signature = getSignature(xml); 
     String uri = "https://" + host + ":" + portStr + "/"; 
     PostMethod postRequest = new PostMethod(uri); 
     postRequest.addRequestHeader("Content-Length", String.valueOf(xml.length())); 
     postRequest.addRequestHeader("Content-Type", "text/xml"); 
     postRequest.addRequestHeader("X-Signature", signature); 
     postRequest.addRequestHeader("X-Username", userName); 
     postRequest.setRequestBody(xml); 
     System.out.println("Sending https request....." + postRequest.toString()); 

     try { 
      client.executeMethod(postRequest); 
     } catch (Exception ex) { 
      throw new TaskExecuteException("Sending post got exception ", ex); 
     } 

     response = postRequest.getResponseBodyAsString(); 
     headers = postRequest.getRequestHeaders(); 
     return response; 
    } 

    public String getPrivateKey() { 
     return privateKey; 
    } 

    public void setPrivateKey(String privateKey) { 
     this.privateKey = privateKey; 
    } 

    public String getHost() { 
     return host; 
    } 

    public void setHost(String host) { 
     this.host = host; 
    } 

    public int getPort() { 
     return port; 
    } 

    public void setPort(int port) { 
     this.port = port; 
    } 

    public String getUserName() { 
     return userName; 
    } 

    public void setUserName(String userName) { 
     this.userName = userName; 
    } 

    public Header[] getHeaders() { 
     return headers; 
    } 

    public void setHeaders(Header[] headers) { 
     this.headers = headers; 
    } 

    public static void main(String[] args) { 

     String privateKey = "your_private_key"; 
     String userName = "your_user_name"; 
     String host = "demo.site.net"; 
     int port = 55443; 

     String xml = 
       "<?xml version='1.0' encoding='UTF-8' standalone='no' ?>" 
       + "<!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>" 
       + "<OPS_envelope>" 
       + "<header>" 
       + "<version>0.9</version>" 
       + "<msg_id>2.21765911726198</msg_id>" 
       + "<msg_type>standard</msg_type>" 
       + "</header>" 
       + "<body>" 
       + "<data_block>" 
       + "<dt_assoc>" 
       + "<item key='attributes'>" 
       + "<dt_assoc>" 
       + "<item key='domain'>test-1061911771844.com</item>" 
       + "<item key='pre-reg'>0</item>" 
       + "</dt_assoc>" 
       + "</item>" 
       + "<item key='object'>DOMAIN</item>" 
       + "<item key='action'>LOOKUP</item>" 
       + "<item key='protocol'>XCP</item>" 
       + "</dt_assoc>" 
       + "</data_block>" 
       + "</body>" 
       + "</OPS_envelope>"; 

     SslClient sslclient = new SslClient(host, port, userName, privateKey); 

     try { 
      String response = sslclient.sendRequest(xml); 
      System.out.println("\nResponse is:\n" + response); 
     } catch (Exception e) { 
      e.printStackTrace(); 

     } 

    } 
} 

如一日,我们正在处理10000+交易,所以在代理插座的数量越来越增加,后2-3天,我们需要做硬重启的Web服务器释放所有与代理服务器的开放式套接字。

HTTPClient是否为SSL握手打开一个套接字,并为实际数据张贴打开另一个套接字?我不这么认为。然后它应该在Web服务器上,而不是在代理服务器上

对于在Web服务器上检查套接字和打开端口,我们使用的是netstat命令。 为了检验在代理服务器插槽和开放的端口,我们使用的是代理工具

当套接字端口用完时,发生事务超时。这个问题的解决方法是调整TIMEWAIT相关的Windows注册表参数:

  • TcpTimedWaitDelay的

  • MaxUserPort的

  • StrictTimeWaitSeqCheck

的TIMEWAIT相关的Windows注册表参数控制套接字端口关闭多长时间后以及多少个端口可用使用。

通过设置这些Windows注册表参数,我已经解决了这个问题,但不知道,天气这是一个正确的解决方案来实现与否。

,当我们在Web服务器检查它创造了只有一个插座。

因为只有一个入站连接。

SO为什么2代理服务器上的套接字。

因为您通过代理服务器连接到两个不同的服务器?

代理套接字的数量正在增加,所以在2-3天后,我们需要重新启动web服务器以释放所有使用代理服务器的开放套接字。

这没有意义。它是具有双重连接的代理服务器,而不是Web服务器。你在上面说过。如果Web服务器的套接字用完了,有人没有正确关闭它们的连接:客户端,代理服务器或Web服务器。可能你的套接字工厂需要重写equals()和hashCode(),以启用任何连接池HttpClient可以做,我不是这方面的专家。

但是您的TrustManager根本上不安全。如果您在生产环境中部署了此功能,那么您已经承诺了主要的安全漏洞。这是目前一个太多的更大的问题,每隔几天就会耗尽套接字。

+0

上面的代码不是完整的产品代码。我们将在finally块中关闭所有套接字和输入,输出流和其他连接变量。所以我没有事情的代码问题。我们如何在OS层释放打开的套接字? – 2012-07-10 09:47:45

+0

@RahulAgrawal显然你不是。很明显,某处存在套接字泄漏。没有这样的事情,以后在操作系统释放打开的套接字。这就是'Socket.close()'的用途。 – EJP 2012-07-10 10:14:30

+0

来自java代码的socket.close()只是向OS发送一个关闭请求。但实际上将管理套接字的操作系统。相同的代码与Linux服务器正常工作,只有Windows服务器有这个问题。在socket.close()之后,我可以使用netstat命令 – 2012-07-10 10:26:25