2017-09-07 4 views
1

pour la vie de moi, je n'arrive pas à comprendre comment obtenir une connexion socket TCP standard pour se reconnecter après un décalage, en particulier dans le contexte d'un IO :: Async :: bouclePerl IO :: Socket :: INET + IO :: Async :: Stream reconnecter au serveur TCP lorsqu'il est déconnecté

quelques notions de base:

#!/usr/bin/perl 
use strict; 
use warnings; 
use Socket; 
use IO::Async::Loop; 
use IO::Async::Stream; 
use IO::Socket; 
use Time::HiRes qw(usleep); 

# standard event loop 
my $loop = IO::Async::Loop->new; 

# notification service socket connection; we only write outgoing 
my $NOTIFY = IO::Socket::INET->new(
    PeerHost => $a_local_network_host, 
    PeerPort => $comm_port, 
    Proto => 'tcp', 
    Type => SOCK_STREAM, 
    ReuseAddr => 1, 
    Blocking => 0 
) or warn("Can't connect to NOTIFY: $!\n"); 
setsockopt($NOTIFY, SOL_SOCKET, SO_KEEPALIVE, 1); 

# main interface element via IO::Async 
my $notifystream = IO::Async::Stream->new(
    handle => $NOTIFY, 
    on_read => sub { 
     my ($self, $buffref, $eof) = @_; 
     # here's where we need to handle $eof if the remote goes away 
     if($eof) { 
      # i have tried several things here 
      usleep(200000); # give the remote service some milliseconds to start back up 
      # process fails if remote is not back up, so i know the timeout is 'good enough' for this test 
      # attempt to reconnect. have also tried recreating from scratch 
      $NOTIFY->connect("$a_local_network_host:$comm_port"); 
      # this doesn't seem to have any effect 
      $self->configure(handle=>$NOTIFY); 
     } 
    } 
); 
$loop->add($notifystream); 

# kickstart the event loop 
$loop->run; 

### -- Meanwhile, elsewhere in the code -- ### 

$notifystream->write("data for notification service\n"); 

en réalité, il y a beaucoup plus de choses qui se passent dans la boucle. J'ai aussi des moyens plus sophistiqués pour tester socket fermé ou cassé, d'autres gestionnaires d'erreurs sur le notifystream $, et un meilleur timeout/backoff pour se reconnecter au service distant, mais cela devrait montrer le coeur de ce que je fais. Lorsque le serveur distant disparaît pour une raison quelconque, je voudrais essayer de me reconnecter sans perturber le reste du système. Dans la plupart des cas, la télécommande envoie l'eOF proprement parce qu'elle redémarre volontairement (ce n'est pas mon choix, juste quelque chose que je dois gérer), mais je voudrais aussi gérer d'autres erreurs de communication. En pratique, le code ci-dessus agit comme s'il fonctionnait, mais le service distant ne reçoit plus d'appels d'écriture pour le notifystream $. aucune erreur n'est générée, le $ notifystream prend heureusement d'autres écritures, mais elles ne sont pas envoyées à la télécommande. Je sens que je me trompe. Je ne cherche pas à réécrire le reste de la boucle d'événements de l'application, donc s'il vous plaît, n'utilisez pas les réponses de type AnyEvent. J'espère vraiment mieux comprendre comment reconnecter/réutiliser/recréer les variables utilisées ici (IO :: Socket :: INET et IO :: Async :: Stream) pour compenser lorsqu'un serveur distant est temporairement indisponible.

Toutes les suggestions ou références vers cet objectif sont les bienvenues. Merci!

- = - = - = - = -

pour résumer les erreurs que j'ai (et n'ont pas) reçues: si je laisse pas usleep, la Reconnect (ou loisirs) de la prise de base échouera en raison de le service distant étant indisponible. Si j'essaie de recréer le socket à partir de rien, puis de 'configurer' le flux, je reçois 'ne peut pas appeler la méthode sysread sur undefined' ce qui me porte à croire que le socket n'est pas recréé correctement. à aucun moment le feu intégré dans le flux «on_read_error» ou «on_write_error» feu, peu importe combien j'écris à la socket avec le code actuel, mais si je détruis complètement le socket, cela va générer une erreur. le socket semble simplement encore actif après que je sais qu'il a fermé, et une reconnexion ne semble pas changer quoi que ce soit. aucune erreur n'est générée, mais le socket n'est pas en cours d'écriture.

est là une syntaxe différente pour se reconnecter à une IO :: Socket socket INET? Jusqu'à présent, les appels à -> connect() ou la reconstruction à partir de zéro semblent être les seules options pour une connexion fermée, et aucun ne semble fonctionner.

+0

Je ne vois aucune vérification d'erreur? Vous mentionnez que vous les avez, alors que disent-ils? Vous auriez certainement besoin de "réactiver" des choses de redémarrage et ces erreurs aideraient à comprendre quoi. – zdim

+0

J'ai 'on_read_error', 'on_write_error' et 'on_writeable_stop' dans le IO :: Async :: Stream - aucun d'entre eux ne se déclenche. du tout. J'ai aussi essayé/catch'd partout - le problème est que rien ne provoque d'erreurs. ça ne marche tout simplement pas. – derelict

+0

Avez-vous essayé de vous reconnecter sur votre '$ NOTIFY'? Il n'est pas clair de la question. Vous pouvez ou ne devez pas encore ajouter à la boucle (et peut-être le redémarrer). Pouvez-vous préciser si vous avez essayé, et pouvez-vous compléter la question avec des informations sur ce que disent les différentes méthodes de vérification des erreurs? – zdim

Répondre

1

Vous ne pouvez simplement pas connecter un socket existant plusieurs fois. Vous ne pouvez fermer le socket et créer un nouveau socket. Cela n'a rien à voir avec IO :: Socket :: INET, IO :: Async :: Stream ou même Perl mais c'est ainsi que fonctionne l'API sockets.

En détail: La prise locale en fait jamais été déconnectée, à savoir qu'il est toujours configuré pour envoyer des données à partir d'une adresse IP locale spécifique et le port et à une adresse spécifique et le port. Seulement, que l'envoi ne fonctionnera plus parce que la connexion TCP sous-jacente est rompue ou fermée (c'est-à-dire que FIN est échangée). Comme il n'y a pas d'API et de délier unconnect une prise la seule façon est de le fermer et de créer un nouveau qui est non liée et sans lien de connexion jusqu'à ce que est appelé.Cette nouvelle socket pourrait alors ou ne pas avoir le même descripteur de fichier que le précédent.