Ответ 1
Я не мог заставить его работать с HttpsUrlConnection
. Но клиент HTTP Apache очень хорошо поддерживает соединение SSL.
Я использую com.sun.net.httpserver.HttpsServer
в моем текущем проекте, который занимается аутентификацией клиента и т.д. В настоящее время он только распечатывает адрес/порт клиентов, так что я могу проверить, используется ли одно TCP-соединение для нескольких запросов (keep-alive
) или если для каждого запроса установлено новое соединение (и, таким образом, каждое новое SSL-рукопожатие делается каждый раз). Когда я использую FireFox, чтобы сделать несколько запросов к серверу, я вижу, что keep-alive работает. Таким образом, часть сервера отлично работает с GET и POST-запросами.
Если я использую HttpURLConnection
для запроса на сервер (в этом случае используется no 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
. Но ничего не изменилось. Проблема все еще существует.
Я не мог заставить его работать с HttpsUrlConnection
. Но клиент HTTP Apache очень хорошо поддерживает соединение SSL.
Я столкнулся с этой же проблемой и, наконец, получил решение после некоторой углубленной отладки.
Http UrlConnection действительно поддерживает Keep-Alive по умолчанию, но сокеты должны быть в очень специфическом состоянии для повторного использования.
Это:
В приведенном выше коде проблема:
conn.setSSLSocketFactory(mySsl.getSocketFactory());
Сохранение результата getSocketFactory() для статической переменной во время инициализации, а затем передача этого значения в conn.setSSLSocketFactory должна позволить повторному использованию сокета.
Установление соединения SSL действительно дорого или для служебных вызовов, или при получении большого количества ресурсов из браузера.
Java Http(s)UrlConnection
обрабатывает HTTP (S) Keep-Alive по умолчанию.
Я не нашел исходный код default SSLSocketFactory и, вероятно, там реализован механизм keep-alive. В качестве подтверждения отключите собственную реализацию SSLSocketFactory
для теста, с настраиваемым хранилищем доверия в javax.net.ssl.trustStore
, чтобы ваш самозаверяющий сертификат был принят.
В соответствии с OpenJDK 7 ServerImpl реализация, которая использует ServerConfig the HttpsServer
, который вы использовали, генерирует постоянный доступ с 5-минутным таймаутом по умолчанию.
Я предлагаю вам установить свойство sun.net.httpserver.debug
на true
server-side, чтобы получить подробную информацию.
Позаботьтесь, чтобы ваш код не добавлял заголовок Connection: close
, который отключает механизм keep-alive.
попробуйте добавить следующий код:
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
Насколько я понимаю HTTP/1.1 и HTTPS, также документально подтвержденный здесь, Keep-Alive
не является сквозным заголовком, а заголовком hop-to-hop, Поскольку SSL включает в себя несколько шагов установления связи между "разными перелетами" (например, CA и сервером) для каждого нового соединения, я думаю, что Keep-Alive
может не применяться в контексте SSL. Итак, вот почему Keep-Alive
заголовок игнорируется с использованием соединений HTTPS. Исходя из этого этого вопроса, вам может потребоваться убедиться, что один экземпляр HTTP-соединения используется для обеспечения наблюдения Keep-Alive
. Кроме того, в этом вопросе кажется, что Apache HTTPClient
является лучшим решением.
Мы можем настроить веб-сервер Apache, добавить следующие директивы, чтобы узнать, поддерживает ли Apache access.log соединение keep-alive для 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, так что, например, "1" означает первый запрос на сохранение после первоначального, "2" - второй и т.д.; в противном случае это всегда 0 (с указанием начального запроса).
Я столкнулся с той же проблемой, и Билл Хили прав. Я проверил свой пример кода ниже с несколькими https-библиотеками. HttpsURLConnection и OKHTTP - это то же самое поведение. Волейбол немного отличается, когда возобновляется сессия, но почти такое же поведение. Надеюсь, это поможет.
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 позволяет сохранить работоспособность. Совместное использование SSLContext и получение факторизации каждого запроса позволяют возобновить сеанс. Я не знаю, как работает TLS-стек, но только подтвердил это поведение соединения с некоторыми мобильными устройствами.
Если вы хотите включить keep-alive среди нескольких классов, вы должны поделиться экземпляром SSLSocketFactory, используя singleton pattern.
Если вы хотите включить возобновление сеанса, убедитесь, что параметры таймаута сеанса достаточно длинные на стороне сервера, такие как SSLSessionCacheTimeout
(apache), ssl_session_timeout
(nginx).