10
votes

Comment détecter une déconnexion de prise TCP (avec prise C Berkeley)

J'utilise une boucle pour lire un message à partir d'une prise C Berkeley, mais je ne suis pas capable de détecter lorsque la prise est déconnectée, je vais donc accepter une nouvelle connexion. S'il vous plaît aider xxx


0 commentaires

3 Réponses :


20
votes

La seule façon de détecter qu'un socket est connecté est en écrivant.

Obtenir une erreur sur Lecture () / RECV () indique que la connexion est cassée, mais ne pas obtenir une erreur lorsque la lecture ne signifie pas que la connexion est en hausse.

Vous pouvez être intéressé à lire ceci: http://lkml.indiana.edu/Hypermail/Linux/kernel/0106.1 /1154.html

En outre, en utilisant TCP Gardez Alive peut aider à distinguer les inactifs et des connexions cassées (en envoyant quelque chose à intervalles réguliers, même s'il n'y a pas de données à envoyer par l'application).

(EDIT: supprimé une phrase incorrecte indiquée par @damon, merci.)


21 commentaires

Légère correction: "Lecture 0 octets peut également dire simplement que la partie distante n'a rien eu à envoyer, sans qu'il s'agisse nécessairement" recevant 0 octets signifie que l'autre extrémité a été propre. arrêt de la connexion. Si l'autre extrémité n'a rien eu à envoyer, vous obtenez de l'aigité ou de l'ewouldblock si la prise est non bloquante, ou qu'il bloque jusqu'à ce que les données arrivent. Une façon de détecter que la connexion est en panne serait d'enregistrer Epollhup et Epollrdhup sur une époll. Ceci n'est pas fiable à 100%, mais signalera des arrêts ordonnés, des demi-arrêts et des conserves manquants.


Bien sûr, un long, long, long temps peut passer avant la détection d'une connexion cassée avec des conserves. Hélas, c'est la nature d'un réseau de paquets indépendants.


@Bruno Le courrier électronique que vous avez lié n'a pas expliqué pourquoi une lecture réussie ne garantit pas que la connexion est en place.


@Pacerier, il n'y a pas de différence entre ne rien obtenir quoi que ce soit parce que rien n'a été envoyé et ne rien obtenir parce que le lien est cassé. (C'est en fait un principe général. Si vous n'obtenez aucune lettre le matin, personne ne vous a fait rien ou que le système postal ne fonctionne pas: vous ne pouvez découvrir que la cause en essayant d'envoyer quelque chose par post, plus ou moins.) C'est pourquoi vous devez gérer les délais d'attente lors de l'utilisation de connexions TCP. Une connexion qui est fermée brusquement ne vous enverra tout simplement pas pour vous dire sa fermeture, car elle ne peut pas.


C'est également pourquoi la plupart des protocoles mettent en œuvre des délimiteurs ou des en-têtes de longueur lorsqu'ils écrivent quelque chose, de sorte que la partie de lecture puisse savoir à quoi s'attendre (E.G. Longueur de contenu En-tête sur le codage de transfert chunté dans http).


@Bruno je veux dire "pourquoi une lecture réussite ne garantit pas la connexion est à la hausse?" Si j'ai reçu mes lettres avec succès , alors le système postal est up non?


@Pacerier, bien sûr, une lecture réussie avec les données vous dira en effet de vous dire que la connexion est en place, le problème est avec une lecture réussie qui ne lit rien (cela dépend également de savoir si vous bloquez à la lecture: vous pouvez bloquer pour toujours si vous ne pouvez pas faire Temps de temps quand rien n'est lu).


@Bruno Sry Je ne suis pas tout à fait sûr que voulez-vous dire par "une lecture réussie qui ne lit rien"? Comment une lecture réussie peut-elle lire rien?


@Pacerier, si vous ne bloquez pas ou s'il y a un délai d'attente (auquel cas vous pourriez essayer de lire à nouveau): la connexion peut toujours être levée sans que la partie distante n'envoie aucune donnée. Ceci est assez courant.


Écrire sur la prise peut être attendre longtemps pour obtenir une erreur en raison du tampon d'envoi. Si votre colis est très petit, la plupart du temps, cela ne fonctionnera pas


@jean, bien sûr qu'il peut y avoir un retard, mais c'est le seul moyen de détecter une déconnexion, toujours. Rincer le tampon si vous devez savoir dès que possible. (Si vous attendez une erreur de lecture, vous pouvez attendre un très long temps ... et cela ne serait pas fiable.)


J'utilise Boost.Asio sur Android, j'utilise async_write pour obtenir l'erreur de déconnexion, mais j'ai trouvé plus de 50%, rien ne se produit après que la connexion soit fermée, même attendant deux minutes. J'ai donc trouvé Stackoverflow.com/a/2939077 et boost.2283326.n4.n.n.fababé.com/... et autre chose, tout ce qu'ils disent utiliser. Donc...


@jean, avec une ASYNC Ecrire, bien sûr, vous devrez peut-être attendre. L'utilisation de la lecture pour détecter la déconnexion ne peut fonctionner que pour des déconnexions propres, pas pour les plus abruptes. C'est une chose TCP, rien à voir avec Boost ou une autre bibliothèque. La réponse que vous liez pour ne pas contredire cela (" si la connexion a traversé un arrêt ordonné [...] et [...] tant que le paquet d'aileron de la Le client est arrivé "). En général, pour gérer ce type de problèmes, vous voudriez que votre application utilise et gérer les délais d'attente en lecture. (+ Cette question concerne de toute façon les prises Berkeley).


S'il s'agit d'une déconnexion abrupte, en utilisant l'écriture également impuissante, non? Qui a besoin de battement de coeur à détecter. Je laisse le client envoyer un package de battement de coeur toutes les 15 secondes, s'il n'y a pas d'emballage, il n'arrive plus de 20 secondes dans le serveur, il déconnecte la connexion. J'utilise la valeur de retour de l'écriture pour détecter la déconnexion dans le client. Mais j'ai trouvé parfois que cela ne fonctionne pas même si je veux des minutes (de nombreux colis envoyés pendant cette période). Je pense donc que vous avez dit: "Détectez qu'un socket est connecté est en écrivant" est problématique.


@jean " S'il s'agit d'une déconnexion abrupte, en utilisant l'écriture également impuissante, non? " Non, ce n'est pas impuissant, il devrait échouer , vous pouvez donc détecter une déconnexion. Votre idée d'attendre de recevoir un battement de coeur toutes les 15 secondes est différente, il pourrait y avoir un délai plus long sans que la connexion soit proche. Vous faites ensuite une décision arbitraire concernant le délai que vous trouvez acceptable, c'est tout (et c'est souvent la bonne chose à faire pratiquement), mais cela ne vous dit pas que la prise était déconnectée, cela signifie simplement que vous abandonnez et suppose que ça a.


Mais cela n'a pas échoué dans mon test. Rien ne se produit lorsque vous écrivez une connexion déconnectée. Au lieu de cela, utilisez des travaux de lecture. Et je ne sais pas quelle est votre définition de la déconnexion abrupte, je le comprends par: débrancher le fil de réseau, la machine à distance de la puissance de la machine ... elle ne pouvait pas détecter par TCP elle-même.


@jean, oui, brusque dans ce sens (perte de paquets complète sans avertissement). Lorsque rien n'est censé être envoyé, vous ne pouvez pas savoir si vous êtes censé recevoir quelque chose (d'où l'idée d'entendeur dans le protocole que vous avez suggéré, pour les protocoles qui le soutiennent). Le seul moyen de savoir si c'est déconnecté est d'essayer d'écrire. Essayer de lire quelque chose si vous ne savez pas si c'est censé avoir envoyé quelque chose ne vous dit rien de toute façon. Votre cas est différent puisque vous utilisez une écriture ASYNC. Je ne connais pas très bien Boost.Asio très bien, mais essayez de rincer le tampon immédiatement si vous le pouvez.


" Il ne pouvait pas détecter par TCP elle-même. ": sans contestation TCP (qui sont une forme d'écriture), non. Une fois la connexion établie, si vous ne savez pas si la partie distante est censée avoir envoyé quelque chose, et si vous essayez de lire et de ne rien obtenir, vous n'obtiendrez rien. Une connexion qui est soudée brusquement d'une manière que sa nageoire ne passe pas simplement ressemblera à une connexion établie.


Je ne comprends pas: "Quand rien n'est censé être envoyé, vous ne pouvez pas savoir si vous êtes censé recevoir quelque chose». Je peux simplement faire: tandis que (1) asyntread (). Lorsque cela renvoie une erreur, je sais déconnecté. Flush Tampon devrait fonctionner, mais c'est une caractéristique du pilote OS, il est inaccessible.


@jean Non, vous ne pouvez pas, c'est le point. Vérifiez le Diagramme de flux TCP . Si vous avez établi une connexion et que vous ne recevez rien du tout, vous ne saurez pas si (a) le câble a été coupé (l'autre côté a-t-il essayé de fermer la connexion en envoyant une nageoire) ou (B ) L'autre côté n'a tout simplement rien eu à envoyer. Si la connexion est établie et si vous ne savez pas si vous souhaitez vous attendre à recevoir quelque chose (vous vous attendez à ce que la connexion soit à priori), vous ne pouvez pas savoir en lisant simplement.


Ok, je t'ai eu. Je veux savoir ce qui se passe écrire dans une connexion qui est une déconnexion brusque? Vous avez dit que cela devrait échouer, car le point distant est inaccessible et le périphérique comme le routeur, le commutateur le détectera et laissez TCP le savoir et de reporter enfin à l'expéditeur?



-1
votes

C'est parce que vous n'avez pas utilisé le délai d'attente Keepalive. Dans la procédure de réception, la prise de courant Keepalive est la meilleure solution pour détecter la connexion morte.

Mais, dans le cas de votre application, continuez à écrire sur la prise, il y a quelque chose à penser plus. Même si vous avez déjà défini une option Keepalive sur 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 TCP de la pile TCP du noyau. TCP_RETRIES1 et TCP_RETRIE2 sont des paramètres de 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 -editor.org/rfc/rfc793.txt p>

Chaque plates-formes contient des configurations de noyau pour la retransmission TCP. P>

netstat -c --timer | grep "192.0.0.1:43245             192.0.68.1:49742"

tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (1.92/0/0)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (0.71/0/0)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (9.46/0/1)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (8.30/0/1)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (7.14/0/1)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (5.98/0/1)
tcp        0      0 192.0.0.1:43245             192.0.68.1:49742            ESTABLISHED keepalive (4.82/0/1)


1 commentaires

Non ce n'est pas le cas. C'est parce qu'il ne parvient pas à vérifier une erreur ou même une fin de flux. Il ignore totalement le résultat renvoyé par rouge () .



0
votes

Votre problème est que vous ignorez complètement le résultat renvoyé par lu () . Votre code après lu () devrait regarder au moins comme ceci: xxx

et accepter une nouvelle connexion doit être effectué dans un thread séparé, non dépendant de la fin du flux. Sur cette connexion.

Le BZERO () L'appel est inutile, juste une solution de contournement pour des erreurs antérieures.


0 commentaires