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)。