Nos clients commencent à voir des centaines de ces "erreurs SSLException - Connexion réinitialisée par un homologue" au cours des dernières semaines et je ne comprends pas pourquoi.
Nous utilisons Retrofit avec okhttp, pas de configuration spéciale
public class OkHttpClientProvider implements IOkHttpClientProvider {
OkHttpClient okHttpClient;
public OkHttpClientProvider() {
this.okHttpClient = createClient();
}
public OkHttpClient getOkHttpClient() {
return this.okHttpClient;
}
private OkHttpClient createClient() {
return new OkHttpClient();
}
}
Le fournisseur de client ci-dessus est un singleton. RestAdapter est construit en utilisant ce client injecté (nous utilisons un poignard) -
RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder()
.setConverter(converter)
.setEndpoint(networkRequestDetails.getServerUrl())
.setClient(new OkClient(okHttpClientProvider.getOkHttpClient()))
.setErrorHandler(new NetworkSynchronousErrorHandler(eventBus))
);
Basé sur des solutions de débordement de pile, ce que j'ai découvert -
La durée de conservation sur le serveur est de 180 secondes, OkHttp a une valeur par défaut de 300 secondes.
Le serveur renvoie "Connection: close" dans son en-tête mais la demande du client envoie "Connection: keepAlive"
Le serveur prend en charge TLS 1.0/1.1/1.2 et utilise Open SSL.
Nos serveurs ont récemment migré vers un autre fournisseur d'hébergement dans une autre région. Je ne sais donc pas s'il s'agit ou non d'échecs DNS.
Nous avons essayé de peaufiner des choses telles que KeepAlive, OpenSSL reconfiguré sur le serveur, mais pour une raison quelconque, le client Android ne cesse de recevoir cette erreur.
Cela se produit immédiatement et sans délai lorsque vous essayez d'utiliser l'application pour publier quelque chose ou tirer pour l'actualiser (elle ne va même pas vers le réseau ou ne dispose pas d'un délai avant que cette exception se produit qui impliquerait que la connexion est déjà rompue). Mais l’essayer plusieurs fois en quelque sorte "le corrige" et nous obtenons un succès. Ça se reproduit plus tard
Nous avons invalidé nos entrées DNS sur le serveur pour voir si c'est ce qui l'a causé mais cela n'a pas aidé
Cela arrive surtout sur LTE mais je l'ai aussi vu en Wifi
Je ne veux pas désactiver Keep Alive car la plupart des clients modernes ne le font pas. De plus, nous utilisons OkHttp 2.4 et ceci pose un problème sur les dispositifs "sandwichs à la crème glacée". J'espère donc que cela devrait régler ces problèmes de réseau sous-jacents. Le client iOS obtient également ces exceptions, mais près de 100 fois moins (le client iOS utilise AFNetworking 2.0). J'ai du mal à trouver de nouvelles choses à essayer à ce stade, de l'aide/des idées?
Mise à jour - Ajout d'une trace de pile complète via okhttp
retrofit.RetrofitError: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.Java:390)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.Java:240)
at Java.lang.reflect.Proxy.invoke(Proxy.Java:397)
at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.Java:76)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.Java:88)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.Java:23)
at Android.os.AsyncTask$2.call(AsyncTask.Java:292)
at Java.util.concurrent.FutureTask.run(FutureTask.Java:237)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1112)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:587)
at Java.lang.Thread.run(Thread.Java:818)
Caused by: javax.net.ssl.SSLException: Read error: ssl=0x9dd07200: I/O error during system call, Connection reset by peer
at com.Android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
at com.Android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.Java:699)
at okio.Okio$2.read(Okio.Java:137)
at okio.AsyncTimeout$2.read(AsyncTimeout.Java:211)
at okio.RealBufferedSource.indexOf(RealBufferedSource.Java:306)
at okio.RealBufferedSource.indexOf(RealBufferedSource.Java:300)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.Java:196)
at com.squareup.okhttp.internal.http.HttpConnection.readResponse(HttpConnection.Java:191)
at com.squareup.okhttp.internal.http.HttpTransport.readResponseHeaders(HttpTransport.Java:80)
at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.Java:917)
at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.Java:793)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.Java:439)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.Java:384)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.Java:497)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.Java:105)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.Java:25)
at retrofit.client.UrlConnectionClient.readResponse(UrlConnectionClient.Java:73)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.Java:38)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.Java:321)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.Java:240)
at Java.lang.reflect.Proxy.invoke(Proxy.Java:397)
at $Proxy15.getAccessTokenUsingResourceOwnerPasswordCredentials(Unknown Source)
at com.company.droid.repository.network.NetworkRepository.getAccessTokenUsingResourceOwnerPasswordCredentials(NetworkRepository.Java:76)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.Java:88)
at com.company.droid.ui.login.LoginTask.doInBackground(LoginTask.Java:23)
at Android.os.AsyncTask$2.call(AsyncTask.Java:292)
at Java.util.concurrent.FutureTask.run(FutureTask.Java:237)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1112)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:587)
at Java.lang.Thread.run(Thread.Java:818)
]}
Récemment, j'ai fait face au problème alors que je travaillais sur un code hérité. Après avoir recherché sur Google, le problème est omniprésent mais sans résolution concrète. J'ai travaillé sur différentes parties du message d'exception et analysé ci-dessous.
Une analyse:
SSLException
: une exception s'est produite avec SSL (Secure Socket Layer), qui est implémenté dans le package javax.net.ssl
du JDK (openJDK/oracleJDK/AndroidSDK
)Read error ssl=# I/O error during system call
: Une erreur s'est produite lors de la lecture à partir du socket sécurisé. Cela s'est produit lors de l'utilisation des bibliothèques/pilotes du système natif. Veuillez noter que toutes les plates-formes solaris, Windows, etc. ont leurs propres bibliothèques de socket qui sont utilisées par le protocole SSL. Windows utilise la bibliothèque WINSOCK.Connection reset by peer
: Ce message est signalé par la bibliothèque système (rapports Solaris ECONNRESET
et Windows WSAECONNRESET
), indiquant que le socket utilisé dans le transfert de données n'est plus utilisable car une connexion existante a été fermée de force par l'hôte distant. Il faut créer un nouveau chemin sécurisé entre l'hôte et le client.Raison:
Comprenant ce problème, j’essaie de trouver la raison de la réinitialisation de la connexion et j’énonce les raisons suivantes:
Network dropped connection on reset(On Windows(WSAENETRESET))
et les opérations suivantes échouent avecConnection reset by peer(On Windows(WSAECONNRESET))
.Résolution:
Connection reset by peer
.Connection reset by peer
.Connection reset by peer
. Voici les termes suggérés pour définir sur différents forums pour résoudre le problème
ConnectionTimeout:
Utilisé uniquement à la fin de la connexion. Si l'hôte met du temps à se connecter, une valeur plus élevée oblige le client à attendre la connexion.SoTimeout
: Socket timeout-Il indique le délai maximum dans lequel le paquet de données est reçu pour considérer la connexion comme active. Si aucune donnée n'est reçue dans le délai imparti, la connexion est supposée être interrompue/interrompue.Linger
: Jusqu'à quelle heure le socket ne doit pas être fermé lorsque des données sont envoyées en file d'attente et que la fonction de socket proche est appelée sur le socket.TcpNoDelay
: Voulez-vous désactiver le tampon qui contient et accumule les paquets TCP et les envoyer une fois qu'un seuil est atteint? Si vous définissez cette valeur sur true, la mise en mémoire tampon TCP est ignorée, de sorte que chaque demande est envoyée immédiatement. Les ralentissements dans le réseau peuvent être dus à une augmentation du trafic réseau due à une transmission de paquets plus petite et plus fréquente.Donc, aucun des paramètres ci-dessus ne permet de garder le réseau en vie et donc inefficace.
J'ai trouvé un paramètre qui peut aider à résoudre le problème qui est ce fonctions
setKeepAlive(true)
setSoKeepalive(HttpParams params, enableKeepalive="true")
Comment ai-je résolu mon problème?
HttpConnectionParams.setSoKeepAlive(params, true)
SSLException
et recherchez le message d'exception pour Connection reset by peer
J'espère que les détails aident. Bonne codage ...
Si vous utilisez Nginx
et que vous rencontrez un problème similaire, cela pourrait aider:
Analysez votre domaine sur ce sslTesturl et voyez si la connexion est autorisée pour la version de votre appareil.
Si les périphériques de version inférieure (tels que <Android 4.4.2, etc.) ne peuvent pas se connecter en raison du support TLS, essayez d'ajouter ceci à votre fichier de configuration Nginx
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
nous avons eu le même problème à partir de ce matin et je l’ai résolu ... espérons que cela aide ...
SSL sur IIS 8
J'espère que cela t'aides!!!
Une autre cause possible de ce message d'erreur est si la méthode HTTP est bloquée par le serveur ou l'équilibreur de charge.
Il semble que la pratique de sécurité standard bloque les méthodes HTTP inutilisées. Nous nous sommes heurtés à ce problème car HEAD était bloqué par l'équilibreur de charge (mais curieusement, tous les serveurs à charge équilibrée ne l'ont pas fait échouer de temps en temps). J'ai pu vérifier que la requête elle-même fonctionnait correctement en la modifiant temporairement pour qu'elle utilise la méthode GET.
Le code d'erreur sur iOS était: Erreur lors de la demande de code d'application: Domaine d'erreur = Code NSURLErrorDomain = -1005 "La connexion réseau a été perdue."
Android Prend en charge la mise en œuvre SSL par défaut sauf pour Android N (API niveau 24) et inférieur à Android 5.1 (API niveau 22)
J'obtenais l'erreur en effectuant l'appel API en dessous des périphériques de niveau API 22 après avoir implémenté SSL au niveau du serveur; c'était lors de la création de l'objet client OkHttpClient, et résolu en ajoutant la méthode connectionSpecs () de la classe OkHttpClient.Builder.
l'erreur reçue était
échec de la réponse: javax.net.ssl.SSLException: établissement de la liaison SSL interrompu: ssl = 0xb8882c00: erreur d'E/S lors de l'appel système, connexion réinitialisée par un homologue
donc j'ai corrigé cela en ajoutant le chèque comme
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop_MR1) {
// Do something for below api level 22
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}
Aussi pour Android N (API niveau 24); Je recevais l'erreur en faisant l'appel HTTP comme
HTTP FAILED: javax.net.ssl.SSLHandshakeException: la négociation a échoué
et cela est corrigé en ajoutant le contrôle pour Android 7 en particulier, comme
if (Android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}
Donc, mon dernier objet OkHttpClient sera comme:
OkHttpClient client
HttpLoggingInterceptor httpLoggingInterceptor2 = new
HttpLoggingInterceptor();
httpLoggingInterceptor2.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder okb = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor2)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request request2 = request.newBuilder().addHeader(AUTH_KEYWORD, AUTH_TYPE_JW + " " + password).build();
return chain.proceed(request2);
}
}).connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS);
if (Android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.N){
// Do something for naugat ; 7
okb.connectionSpecs(Collections.singletonList(getSpec()));
}
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.Lollipop_MR1) {
List<ConnectionSpec> specsList = getSpecsBelowLollipopMR1(okb);
if (specsList != null) {
okb.connectionSpecs(specsList);
}
}
//init client
client = okb.build();
getSpecsBelowLollipopMR1 fonctionne comme,
private List<ConnectionSpec> getSpecsBelowLollipopMR1(OkHttpClient.Builder okb) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
okb.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
return specs;
} catch (Exception exc) {
Timber.e("OkHttpTLSCompat Error while setting TLS 1.2"+ exc);
return null;
}
}
La classe Tls12SocketFactory se trouve dans le lien ci-dessous (commentaire de gotev):
https://github.com/square/okhttp/issues/2372
Pour plus de soutien en ajoutant quelques liens ci-dessous cela vous aidera en détail,
https://developer.Android.com/training/articles/security-ssl