我在我当前的项目中使用com.sun.net.httpserver.HttpsServer
处理客户端身份验证等。目前它只打印出客户端地址/端口,以便我可以检查是否有一个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
。但没有改变。问题依然存在。
答案 0 :(得分:17)
我遇到了同样的问题,经过一些深入的调试后终于找到了解决方案。
Http(s)UrlConnection默认处理Keep-Alive,但套接字必须处于非常特定的状态才能重复使用。
这些是:
在上面的代码中,问题是:
conn.setSSLSocketFactory(mySsl.getSocketFactory());
在初始化期间将getSocketFactory()的结果保存到静态变量,然后将其传递给conn.setSSLSocketFactory应该允许重用套接字。
答案 1 :(得分:5)
我无法使用HttpsUrlConnection
。但Apache的HTTP客户端可以很好地处理SSL连接的保持活动状态。
答案 2 :(得分:2)
对于服务调用或从浏览器获取许多资源,SSL连接建立非常昂贵。
Java Http(s)UrlConnection
处理HTTP(S)Keep-Alive by default。
我还没有找到default SSLSocketFactory的源代码,可能还有实现了keep-alive机制。作为确认,请在SSLSocketFactory
中使用自定义信任存储禁用您自己的javax.net.ssl.trustStore
测试实施,以便接受您的自签名证书。
根据使用ServerImpl的OpenJDK 7 ServerConfig实现,您使用的HttpsServer
会发出保持活动状态,默认情况下会超时5分钟。
我建议您将属性sun.net.httpserver.debug
设置为true
服务器端以获取详细信息。
请注意,您的代码不会添加禁用保持活动机制的标头Connection: close
。
答案 3 :(得分:0)
据我所知HTTP/1.1和HTTPS协议,还记录了here,Keep-Alive
不是端到端标头但跳到跳标头。由于SSL涉及每个新连接的“不同跃点”(例如CA和服务器)之间的多个握手步骤,我认为Keep-Alive
可能不适用于SSL上下文。因此,使用HTTPS连接会忽略that can be why Keep-Alive
标头。根据此this question,您可能需要确保一个 HTTP连接实例用于保证Keep-Alive
观察。此外,在这个问题中,似乎Apache HTTPClient
是一个更好的解决方案。
答案 4 :(得分:0)
尝试添加以下代码:
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
答案 5 :(得分:0)
我们可能会设置一个Apache Web服务器,添加以下指令以查看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(表示初始请求)。
答案 6 :(得分: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)。
答案 7 :(得分:0)
除了@Bill Healey回答之外,还必须将HostnameVerifier声明为静态。 我尝试了几种模式,无论是否关闭输入流和连接,它们对我来说都没有改变。唯一重要的是提到的属性的静态声明。
/**
SSLSocketFactory and HostnameVerifier must be declared static in order to be able to use keep-alive option
*/
private static SSLSocketFactory factory = null;
private static HostnameVerifier hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
public static void prepareForCustomTrustIfNeeded(HttpsURLConnection connection) {
try {
if(factory == null) {
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, customTrustedCerts, new SecureRandom());
factory = sslc.getSocketFactory();
}
connection.setSSLSocketFactory(factory);
connection.setHostnameVerifier(hostnameVerifier);
} catch (Exception e) {
e.printStackTrace();
}
}