2010-04-30 6 views
2

Je suis actuellement en train d'essayer de réparer un bogue dans un serveur proxy que j'ai écrit à propos de l'appel socket select(). J'utilise les bibliothèques Poco C++ (en utilisant SocketReactor) et le problème est en fait dans le code Poco qui peut être un bug mais je n'ai pas encore reçu de confirmation de leur part. Que se passe-t-il lorsqu'une connexion interrompt brusquement l'appel socket() renvoie immédiatement ce qui est ce que je crois qu'il est censé faire? Quoi qu'il en soit, il renvoie toutes les sockets déconnectées dans l'ensemble lisible des descripteurs de fichiers mais le problème est qu'une exception "Socket is not connected" est levée quand Poco essaye de déclencher le gestionnaire d'événement onReadable qui est où je mettrais le code occupe-toi de ça. Étant donné que l'exception est capturée silencieusement et que l'événement onReadable n'est jamais déclenché, l'appel select() continue à renvoyer immédiatement, ce qui entraîne une boucle infinie dans le SocketReactor. Je pensais modifier le code Poco afin que plutôt que d'attraper l'exception silencieusement, il déclenche un nouvel événement appelé onDisconnected ou quelque chose comme ça afin qu'un nettoyage puisse être effectué.Socket select() Gestion des déconnexions abruptes

Ma question est, existe-t-il des moyens élégants de déterminer si un socket s'est fermé anormalement en utilisant select()? Je pensais utiliser le message d'exception pour déterminer quand cela s'est produit mais cela me semble sale.

+1

Avez-vous enregistré des gestionnaires pour l'événement ErrorNotification? – nos

+0

Oui, aucun événement d'erreur n'est déclenché. – Genesis

+0

Je ne sais rien à propos de Poco, mais en général, select() ne signale aucune différenciation entre un socket lisible et un socket déconnecté. Il signale qu'un socket est lisible lorsqu'il est fermé/perdu/terminé. Une lecture ultérieure sur le socket indique alors si une déconnexion s'est produite et si elle était gracieuse ou anormale. Il semble que Poco détecte une déconnexion anormale lors de la lecture du socket (sauf l'exception), mais au lieu de l'avaler et de revenir à select(), il devrait arrêter son traitement du socket à la place. Cela suggère définitivement un bug à l'intérieur de Poco pour moi. –

Répondre

1

Il semble que vous ayez raison Remy. J'ai réussi à distinguer si la prise était coupée à l'aide du code ci-dessous (ceci a été ajouté à Poco/Net/src/SocketImpl.cpp):

bool SocketImpl::isConnected() 
{ 
int bytestoread; 
int rc; 
fd_set fdRead; 

FD_ZERO(&fdRead); 
FD_SET(_sockfd, &fdRead); 

struct timeval tv; 
tv.tv_sec = 0; 
tv.tv_usec = 250000; 

rc = ::select(int(_sockfd) + 1, &fdRead, (fd_set*) 0, (fd_set*) 0, &tv); 
ioctl(FIONREAD, &bytestoread); 

return !((bytestoread == 0) && (rc == 1)); 
} 

De ma compréhension, ce vérifie si la prise est lisible au moyen d'un appel pour sélectionner(), puis vérifie le nombre réel d'octets disponibles sur ce socket. Si le socket signale qu'il est lisible mais que les octets sont 0 alors le socket n'est pas réellement connecté.

Bien que cela réponde à ma question ici, cela n'a malheureusement pas résolu mon problème Poco car je ne peux pas trouver un moyen de résoudre ce problème dans le code Poco SocketReactor. J'ai essayé de créer un nouvel événement appelé DisconnectNotification mais malheureusement je ne peux pas l'appeler car la même erreur est levée comme pour une ReadNotification sur un socket fermé.

2

J'ai eu le même problème. La seule façon de contourner le problème est de contrôler le code de sortie des applications clientes. La solution que j'ai utilisée était d'envoyer un signal d'arrêt avant que le réacteur ne soit terminé côté client. Ensuite, sur le serveur, vous fermez simplement le socket.

//Client: 
//Handler Class: onWrite 
Packet p = Packet::Shutdown(); 

if (p.fn == "shutdown") 
{ 
    _reactor.stop(); 
    delete this; 
} 

//Server 
//Accepter Class: onRead 
if (p.fn == "shutdown") 
{ 
    printf("%s has disconnected", _username.c_str()); 
    _socket.close(); 
    delete this; 
} 
1

Juste attraper les ConnectionResetException dans OnReadable() (traite la ReadableNotification) Ensuite, il gère « Connexion réinitialisée par les pairs » correctement.

catch(Poco::Net::ConnectionResetException &ex) 
{ 
    _socket.shutdownSend(); 
    delete this; 
}