HttpsUrlConnection和keep-alive

我在我当前的项目中使用com.sun.net.httpserver.HttpsServer处理客户端身份validation等。目前它只打印出客户端地址/端口,以便我可以检查是否有一个TCP连接用于多个请求( keep-alive )或者是否为每个请求建立了新连接(因此每次都进行新的SSL握手)。 当我使用FireFox对服务器发出多个请求时,我可以看到keep-alive正在运行。 因此,服务器部分可以正常使用GET和POST请求。

如果我使用HttpURLConnection对服务器发出请求(在这种情况下不使用SSL), keep-alive也会起作用:只为多个顺序启动的请求建立一个连接。

但是,如果我使用HttpsURLConnection (使用完全相同的代码,但使用 SSL),那么keep-alive将不再起作用。 因此,对于每个请求,都建立了一个新连接,尽管我使用相同的SSLContext (和SSLSocketFactory ):

 // URL myUrl = ... // SSLContext mySsl = ... HttpsURLConnection conn = (HttpsURLConnection) myUrl.openConnection(); conn.setUseCaches(false); conn.setSSLSocketFactory(mySsl.getSocketFactory()); conn.setRequestMethod("POST"); // send Data // receive Data 

如何强制HttpsURLConnection使用keep-alive因为许多请求会导致许多SSL握手,这是一个真正的性能问题?

更新(2012-04-02):我没有每次调用mySsl.getSocketFactory() ,而是尝试缓存SSLSocketFactory 。 但没有改变。 问题依然存在。

我遇到了这个完全相同的问题,经过一些深入的调试后终于找到了解决方案。

Http(s)UrlConnection默认处理Keep-Alive,但套接字必须处于非常特定的状态才能重用。

这些是:

  • 输入流必须完全消耗。 您必须在输入流上调用read,直到它返回-1并关闭它。
  • 底层套接字上的设置必须使用完全相同的对象。
  • 完成后,你应该在Http(s)URLConnection上调用disconnect(是的,这是违反直觉的)。

在上面的代码中,问题是:

 conn.setSSLSocketFactory(mySsl.getSocketFactory()); 

在初始化期间将getSocketFactory()的结果保存到静态变量,然后将其传递给conn.setSSLSocketFactory应该允许重用套接字。

我无法使用HttpsUrlConnection 。 但Apache的HTTP客户端可以很好地处理SSL连接的保持活动状态。

无论是对于服务调用还是从浏览器获取许多资源,SSL连接建立都非常昂贵。

Java Http(s)UrlConnection处理HTTP(S) Keep-Alive 。

我还没有找到默认SSLSocketFactory的源代码,并且可能在那里实现了keep-alive机制。 作为确认,请在javax.net.ssl.trustStore使用自定义信任存储禁用您自己的SSLSocketFactory实现以进行测试,以便接受您的自签名证书。

根据使用ServerConfig的OpenJDK 7 ServerImpl实现,您使用的HttpsServer发出保持活动状态,默认情况下超时为5分钟。

我建议你将属性sun.net.httpserver.debug设置为true服务器端以获取详细信息。

注意你的代码不添加标题Connection: close会禁用keep-alive机制。

尝试添加以下代码:

 con.setRequestProperty("Connection", "Keep-Alive"); con.setRequestProperty("Keep-Alive", "header"); 

据我所知, 此处还记载了 HTTP / 1.1和HTTPS协议, Keep-Alive不是端到端头,而是跳到跳头。 由于SSL涉及每个新连接的“不同跃点”(例如CA和服务器)之间的多个握手步骤,我认为Keep-Alive可能不适用于SSL上下文。 因此, 这可能是使用HTTPS连接忽略Keep-Alive标头的原因 。 基于此问题 ,您可能需要确保使用一个 HTTP连接实例来保证Keep-Alive观察。 此外,在这个问题中,Apache HTTPClient似乎是一个更好的解决方案。

我们可以设置Apache Webserver,添加以下指令以查看Apache的access.log是否具有http客户端的保持连接。

 LogFormat "%k %v %h %l %u %t \"%r\" %>s %b" common CustomLog "logs/access.log" common 

http://httpd.apache.org/docs/current/mod/mod_log_config.html

“%k”此连接上处理的keepalive请求数。 如果正在使用KeepAlive,那么有趣的是,例如,’1’表示在初始请求之后的第一个keepalive请求,’2’表示第二个,等等…; 否则这始终为0(表示初始请求)。

我遇到了同样的问题,比尔希利是对的。 我用几个https库测试了下面的示例代码。 HttpsURLConnection和OKHTTP是完全相同的行为。 当会话恢复时,Volley有点不同,但几乎相同的行为。 我希望这会有所帮助。

 public class SampleActivity extends Activity implements OnClickListener { // Keep default context and factory private SSLContext mDefaultSslContext; private SSLSocketFactory mDefaultSslFactory; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); findViewById(R.id.button_id).setOnClickListener(this); try { // Initialize context and factory mDefaultSslContext = SSLContext.getInstance("TLS"); mDefaultSslContext.init(null, null, null); mDefaultSslFactory = mDefaultSslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { Log.e(TAG, e.getMessage(), e); } } @Override public void onClick(View v){ SSLContext sslcontext; SSLSocketFactory sslfactory; try { // If using this factory, enable Keep-Alive sslfactory = mDefaultSslFactory; // If using this factory, enable session resumption (abbreviated handshake) sslfactory = mDefaultSslContext.getSocketFactory(); // If using this factory, enable full handshake each time sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, null, null); sslfactory = sslcontext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { Log.e(TAG, e.getMessage(), e); } URL url = new URL("https://example.com"); HttpsURLConnection = conn = (HttpsURLConnection) url.openConnection(); conn.setSSLSocketFactory(sslfactory); conn.connect(); } } 

更新:

共享SSLSocketFactory可以实现keep-alive。 共享SSLContext并获取facotry每个请求启用会话恢复。 我不知道TLS堆栈是如何工作的,但只是确认了一些移动设备的这些连接行为。

如果要在多个类之间启用keep-alive,则应使用singleton模式共享SSLSocketFactory的实例。

如果要启用会话恢复,请确保服务器端的会话超时设置足够长,例如SSLSessionCacheTimeout (apache), ssl_session_timeout (nginx)。