現在のプロジェクトでは、クライアント認証などを扱う_com.Sun.net.httpserver.HttpsServer
_を使用しています。現在、クライアントのアドレス/ポートのみが出力されるため、1つのTCP接続が複数のリクエストに使用されているかどうかを確認できます(_keep-alive
_)またはすべての要求に対して新しい接続が確立された場合(したがって、新しいSSLハンドシェイクが毎回行われます)。 FireFoxを使用してサーバーに対して複数の要求を行うと、キープアライブが機能していることがわかります。したがって、サーバー部分はGETおよびPOSTリクエストで正常に機能します。
HttpURLConnection
を使用してサーバーに対してリクエストを作成する場合(この場合はno SSLを使用)_keep-alive
_も機能します:複数の順次開始されたリクエストに対して1つの接続のみが確立されます。
しかし、HttpsURLConnection
(まったく同じコードを使用しますが、sing 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
で動作させることができませんでした。しかし、ApacheのHTTPクライアントは、SSL接続でのキープアライブを非常にうまく処理します。
私はこのまったく同じ問題に遭遇し、いくつかの詳細なデバッグの後に最終的に解決策を見つけました。
Http(s)UrlConnectionはデフォルトでKeep-Aliveを処理しますが、ソケットを再利用するには非常に特殊な状態にする必要があります。
これらは:
上記のコードでは、問題は次のとおりです。
conn.setSSLSocketFactory(mySsl.getSocketFactory());
初期化中にgetSocketFactory()の結果を静的変数に保存し、それをconn.setSSLSocketFactoryに渡すことで、ソケットを再利用できます。
SSL接続の確立は、サービスの呼び出しまたはブラウザーから多くのリソースを取得する場合に非常に高価です。
Java Http(s)UrlConnection
はHTTP(S)を処理します デフォルトでキープアライブ 。
デフォルトSSLSocketFactory のソースコードが見つかりませんでした。おそらく、キープアライブメカニズムが実装されています。確認として、テスト用に独自のSSLSocketFactory
実装を無効にし、javax.net.ssl.trustStore
自己署名証明書が受け入れられるようにします。
OpenJDK 7によれば ServerImpl を使用する実装 ServerConfig 使用したHttpsServer
は、デフォルトで5分のタイムアウトのキープアライブを発行します。
プロパティの設定Sun.net.httpserver.debug
からtrue
サーバー側に移動して詳細を取得します。
コードがヘッダーを追加しないように注意してくださいConnection: close
キープアライブメカニズムを無効にします。
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が使用されている場合、たとえば、「1」は最初のキープアライブリクエストを意味し、「2」は2番目などを意味します。それ以外の場合、これは常に0(最初の要求を示す)です。
次のコードを追加してみてください。
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Keep-Alive", "header");
私が理解できる限り、 HTTP/1.1 および [〜#〜] https [〜#〜] プロトコル、文書化されています here 、Keep-Alive
はend-to-endヘッダーではなく、hop-to-hopヘッダー。 SSLには、新しい接続ごとに「異なるホップ」(CAとサーバーなど)間で複数のステップのハンドシェークが含まれるため、Keep-Alive
はSSLコンテキストでは適用されない場合があります。したがって、- それが理由である可能性がありますKeep-Alive
ヘッダーはHTTPS接続を使用して無視されます。これに基づいて この質問 を確認する必要があるかもしれませんoneHTTP接続のインスタンスがKeep-Alive
観察。また、質問では、Apache HTTPClient
がより良い解決策であるようです。
私は同じ問題に直面しました、そしてビル・ヒーリーは正しいです。以下のサンプルコードをいくつかの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スタックの仕組みはわかりませんが、一部のモバイルデバイスでのこれらの接続動作を確認しただけです。
複数のクラス間でキープアライブを有効にする場合は、シングルトンパターンを使用してSSLSocketFactoryのインスタンスを共有する必要があります。
セッションの再開を有効にする場合は、SSLSessionCacheTimeout
(Apache)、ssl_session_timeout
(nginx)。