6
votes

So_keekeekeealive ne fonctionne pas lors d'un appel à écrire ()?

Je développe une application de socket, qui doit être robuste à des pannes de réseau.

L'application dispose de 2 threads exécutés, un message d'attente de la prise (une boucle de lecture ()) et des autres messages d'envoi à la prise (une boucle d'écriture ()).

Je tente actuellement d'utiliser So_ekeekeealive pour gérer les défaillances du réseau. Cela fonctionne bien si je ne suis que bloqué sur Lecture (). Quelques secondes après la perte de la connexion (câble réseau supprimé), LIRE () échouera avec le message "Connexion expiré".

Mais, si j'essaie de wrte () après que le réseau est déconnecté () après que le réseau soit débranché ( et avant la fin du délai d'attente), les deux écrits () et lire () bloqueront pour toujours, sans erreur.

Il s'agit d'un code d'exemple dépouillé qui dirige STDIN / STDOUT sur la prise. Il écoute sur le port 5656: xxx

pour reproduire l'erreur, utilisez Telnet 5656. Si vous quittez après quelques secondes d'exploitation après que la connexion soit perdue, à moins que j'essaie d'écrire quelque chose dans le terminal. Dans ce cas, il bloquera pour toujours.

Ainsi, les questions sont: Qu'est-ce qui ne va pas? comment le réparer? Y a-t-il d'autres alternatives?

merci!


J'ai essayé d'utiliser Wireshark pour inspecter la connexion réseau. Si je n'appelle pas d'écriture (), je peux voir les paquets TCP Garden-Vive envoyés et la connexion est proche après quelques secondes.

Si, à la place, j'essaie d'écrire (), il cesse d'envoyer les paquets de conservation et commence à envoyer à la place des retransmissions TCP (cela me semble ok). Le problème est que le temps entre les retransmissions augmente plus grand et plus grand après chaque échec, et il semble ne jamais abandonner et fermer la prise.

existe un moyen de définir le nombre maximum de retransmissions, ou Quelque chose de similaire? Merci


0 commentaires

6 Réponses :


2
votes

Je ne sais pas si quelqu'un d'autre vous donnera une meilleure alternative, mais dans plusieurs projets, nous avons participé à des situations très similaires.

Pour nous, la solution consistait à simplement prendre le contrôle entre vos mains et ne pas compter sur le système d'exploitation / pilotes sous-jacents pour vous dire lorsque la connexion meurt. Si vous contrôlez les côtés du client et du serveur, vous pouvez introduire vos propres messages ping qui rebondissent entre le client et le serveur. De cette façon, vous pouvez un) contrôler vos propres délais de connexion et B) Gardez facilement un enregistrement indiquant la santé de la connexion.

Dans la dernière application, nous avons caché ces pings sous forme de messages de contrôle dans la bande dans la bibliothèque de communication elle-même, de sorte que le code d'application client / serveur réel était concerné, les délais de connexion viennent de travailler.


1 commentaires

Je l'aime, mais je mettez en place un seul côté d'un protocole existant, qui n'a pas de moyen de forcer des «pings».



0
votes

in write_daemon () , vous stockez la valeur de retour de écrire () dans la variable ret2 , mais vérifiez ensuite une prise de courant Erreur lors de l'utilisation de la variable RET à la place, vous ne attrapez donc jamais aucun écrire () erreurs.


1 commentaires

Merci! Je l'ai réparé. Malheureusement, cela n'a pas corrigé le problème.



4
votes

J'ai trouvé l'option TCP_USER_Timeout Socket (RFC5482), qui ferme la connexion si les données envoyées ne sont pas ACK'ED après l'intervalle spécifié.

Cela fonctionne bien pour moi =) P>

//defined in include/uapi/linux/tcp.h (since Linux 2.6.37)
#define TCP_USER_TIMEOUT 18

int tcp_timeout        =10000; //10 seconds before aborting a write()

result = setsockopt(socket_fd, SOL_TCP, TCP_USER_TIMEOUT, &tcp_timeout, sizeof(int));
if (result < 0) 
    error("TCP_USER_TIMEOUT");


1 commentaires

Ce n'est pas un bogue, tcp_user_timeout a été implémenté pour résoudre le même problème que vous avez décrit ci-dessus, c'est-à-dire dans le cas où l'envoi de paquets Keepalive, si cela est envoyé sur cette prise, puis la ré-transmission La minuterie commence et la déconnexion est apprise après 15-20 minutes! Utilisation de TCP_USER_TIMEOUT , met une limite difficile sur la durée de la durée d'un paquet de NON ACK'D.



1
votes

TCP Garder Alive est spécifié dans RFC1122 . La fonction GARDER ALIVE de TCP n'est pas de détecter des pannes de réseau à court terme, mais plutôt de nettoyer les blocs / tampons de contrôle TCP pouvant utiliser des ressources précieuses. Ce RFC a également été écrit en 1989. Le RFC stipule explicitement que TCP maintient les vivants ne doit pas être envoyé plus d'une fois toutes les deux heures, et ensuite, il n'est nécessaire que s'il n'y avait pas d'autre trafic. Si un protocole de niveau supérieur doit détecter une perte de connexion, il s'agit du travail du protocole de niveau supérieur de le faire lui-même. Le protocole de routage BGP, qui fonctionne au-dessus de TCP, envoie sa propre forme de conserver un message vivant une fois toutes les 60 secondes par défaut. La spécification BGP indique qu'une connexion doit être considérée comme morte s'il n'y a pas eu de nouveau trafic vu dans les 3 dernières secondes de Keep_alive_Interval. OpenSSH met en œuvre sa propre façon de rester en vie sous la forme d'un ping et de pong. Il réessayera l'envoi de x Pings qu'il attend une réponse (pong) dans l'autre ou qu'il tue la connexion. TCP lui-même essaie vraiment de fournir des données face aux pannes de réseau temporaire et n'est pas utile de détecter la panne de réseau.

Normalement, si vous souhaitez implémenter une maintenance en vie et que vous souhaitez éviter de bloquer, on passerait à des E / S non bloquants et maintenez une minuterie pour laquelle peut être utilisé avec des appels sélectionnés () / sondage () avec un délai d'attente . Une autre option pourrait être d'utiliser un fil de minuterie distinct ou même une approche plus brute d'utiliser Sigalarm. Je recommande d'utiliser le O_NONBLOCK avec FCNTL () pour définir la prise sur les E / S non bloquantes. Vous pouvez ensuite utiliser GetTimeOfday () pour enregistrer lors de l'enregistrement d'E / S entrant et dormez avec SELECT () jusqu'à ce que l'une ou l'autre voie suivante soit en vie soit due ou que les E / S se produisent.


1 commentaires

Pas tout à fait ce que tu as dit. RFC 1122 stipule que l'intervalle par défaut de la première enquête de garde à suivre ne devrait pas être inférieur à deux heures. Cela signifie que l'utilisateur peut la définir sur ce qu'il veut.



1
votes

Avez-vous reçu succinctement un octet ou un ACK de l'autre côté avant de déconnecter le câble? Peut-être que ceci est lié au comportement décrit dans http: //lkml.indiana. EDU / HYPERMAIL / Linux / Kernel / 0508.2 / 0757.HTML :


Votre cas de test est discutable, car vous ne recevez même pas une ACK en état établi, la variable TP-> RCV_TSTAMP n'a aucun moyen d'être initialisée. Le seul ACK que vous recevez est celui qui répond à la Syn de configuration de la connexion et nous n'initialons pas TP-> RCV_STAMP pour cet ack.

Les chèques de temps de conserve nécessitent absolument que TP-> RCV_TSTAMP ait une valeur valide et jusqu'à ce que vous traitez un ack en état établi, il ne le fait pas.

Si vous envoyez avec succès ou recevez avec succès au moins un octet sur la connexion et traitez donc au moins une ACK en état établi, je pense que vous constaterez que les gardereaux se comportent correctement.


C'est un comportement So_ekeekalive obscur.


0 commentaires

0
votes

C'est à cause de la retransmission TCP actionnée par la pile TCP sans votre conscience. Voici des solutions.

Même si vous avez déjà défini une option Keepalive à votre prise d'applications, vous ne pouvez pas détecter dans le temps l'état de connexion morte de la prise, dans le cas de votre application permet d'écrire sur la prise. C'est à cause de la retransmission de TCP par la pile TCP du noyau. TCP_RETRIES1 et TCP_RETRIES2 sont des paramètres du noyau pour la configuration du délai de retransmission TCP. Il est difficile de prédire le temps précis du délai de retransmission, car il est calculé par le mécanisme RTT. Vous pouvez voir ce calcul dans RFC793. (3.7. Communication de données)

HTTPS: //www.rfc -editor.org/rfc/rfc793.txt

Chaque platetterie dispose de configurations de noyau pour la retransmission TCP. xxx

http://linux.die.net/man/7/tcp xxx

http://www.hpuxtips.es/?q=node / 53 xxx

http://www-903.ibm.com/kr/event/download/200804_324_swma/socket.pdf

Vous devez définir une valeur inférieure pour tcp_retries2 (par défaut 15) Si vous voulez détecter tôt la connexion morte, mais ce n'est pas un moment précis que j'ai déjà dit. De plus, vous ne pouvez actuellement pas définir ces valeurs que pour une prise unique. Ce sont des paramètres mondiaux du noyau. Il y avait un essai pour appliquer une option de prise de récupération TCP pour une prise unique ( http://patchwork.ozlabs.org. / Patch / 55236 / ), mais je ne pense pas que cela a été appliqué dans le noyau Mainline. Je ne trouve pas ces options Définition dans les fichiers d'en-tête système.

Pour référence, vous pouvez surveiller votre option de prise Keepalive via "NetStat --Timers" comme ci-dessous. https://stackoverflow.com/questions/34914278 xxx

De plus, lorsque vous pouvez rencontrer des événements de retour différents en fonction des plateformes que vous utilisez, Donc, vous ne devez pas décider du statut de connexion morte uniquement par des événements de retour. Par exemple, HP renvoie l'événement PollerR et Aix renvoie juste un événement pollin lorsque le délai d'attente de conserve se produit. Vous rencontrerez une erreur eTimeDout dans RECV () appelez à cette époque.

dans la dernière version du noyau (puisque 2.6.37), vous pouvez utiliser TCP_USER_Timeout Option fonctionnera bien. Cette option peut être utilisée pour une prise unique.


0 commentaires