Comment faire proprement reconnecter un boost::socket à la suite d'une déconnexion?
Mon application cliente utilise un boost::asio::ip::tcp::socket
pour se connecter à un serveur distant.
Si l'application perd la connexion à ce serveur (en raison par exemple du serveur en panne ou fermeture), j'aimerais qu'elle pour tenter de se re-connecter à intervalles réguliers jusqu'à ce qu'il réussisse.
Que dois-je faire sur le côté client à proprement gérer une déconnexion, tidy up et alors à plusieurs reprises tentative de se reconnecte?
Actuellement bits intéressants de mon code ressemble à quelque chose comme ça.
Je connect
comme ceci:
bool MyClient::myconnect()
{
bool isConnected = false;
//Attempt connection
socket.connect(server_endpoint, errorcode);
if (errorcode)
{
cerr << "Connection failed: " << errorcode.message() << endl;
mydisconnect();
}
else
{
isConnected = true;
//Connected so setup async read for an incoming message.
startReadMessage();
//And start the io_service_thread
io_service_thread = new boost::thread(
boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
}
return (isConnected)
}
Où la runIOServer()
méthode est juste:
void MyClient::runIOService(boost::asio::io_service& io_service)
{
size_t executedCount = io_service.run();
cout << "io_service: " << executedCount << " handlers executed." << endl;
io_service.reset();
}
Et si l'un des async lire gestionnaires de renvoyer un message d'erreur, puis ils ont juste appeler cette disconnect
méthode:
void MyClient::mydisconnect(void)
{
boost::system::error_code errorcode;
if (socket.is_open())
{
//Boost documentation recommends calling shutdown first
//for "graceful" closing of socket.
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
if (errorcode)
{
cerr << "socket.shutdown error: " << errorcode.message() << endl;
}
socket.close(errorcode);
if (errorcode)
{
cerr << "socket.close error: " << errorcode.message() << endl;
}
//Notify the observer we have disconnected
myObserver->disconnected();
}
..qui tente de mettre gracieusement débranchez puis en informe l'observateur, ce qui permettra de lancer l'appel connect()
à cinq secondes jusqu'à ce qu'il obtient reconnecté.
Est-il autre chose que je dois faire?
Actuellement, ce n' semblent de travail. Si je tue le serveur auquel il est connecté à je obtenir attendus "End of file"
erreur lors de ma lecture des gestionnaires et mydisconnect()
est appelée sans aucun problème.
Mais quand il tente alors de se re-connecter et échoue je voir rapport "socket.shutdown error: Invalid argument"
. Est-ce juste parce que je suis tenter de fermer un socket qui n'est pas en lecture/écritures en attente sur elle? Ou est-il quelque chose de plus?
- Quel est votre justification de l'appel de l'arrêt, si vous avez déjà détecté l'autre extrémité de la connexion fermée?
- N'est-ce pas recommandé? Je pensais que il peut y avoir des opérations en cours sur le socket qui doivent être annulées avec un
shutdown()
. J'ai surtout tout faire pour la simplicité: le mêmemydisconnect()
méthode est appelée si je veux déconnecter normalement, ou si l'une des opérations asynchrones retourner une erreur. - Je ne sais pas si c'est recommandé ou pas. Où serait l'attente de données ou d'opérations? L'autre extrémité de la connexion n'est pas là.
- Il n'est pas nécessaire. Il est recommandé dans un largement méconnu l'article MSDN dont le but est de vous montrer comment réaliser synchronisée se ferme par deux pairs, mais dans ce cas vous ne devez arrêt pour la sortie.
close()
est tout ce qui est nécessaire. Des données sont encore en vol est toujours livré.
Vous devez vous connecter pour publier un commentaire.
Vous avez besoin pour créer un nouveau
boost::asio::ip::tcp::socket
chaque fois que vous vous reconnectez. La façon la plus simple de le faire est probablement juste de répartir la prise sur le tas à l'aide d'unboost::shared_ptr
(vous pourriez probablement aussi sortir avecscoped_ptr
si votre prise est entièrement encapsulé dans une classe). E. g.:Puis, quand
mydisconnect
est appelé, vous pouvez libérer le socket:L'erreur que vous voyez est probablement une conséquence de l'OS nettoyer le descripteur de fichier après que vous avez appelé
close
. Lorsque vous appelezclose
et puis essayer deconnect
sur le même socket, vous êtes probablement en train de connecter un descripteur de fichier invalide. À ce stade, vous devriez voir un message d'erreur commençant par "échec de la Connexion: ..." basé sur votre logique, mais vous appelezmydisconnect
qui est probablement alors de tenter d'appelershutdown
sur un descripteur de fichier invalide. Cercle vicieux!.reset
sur leshared_ptr
comme vous le décrivez. Si la tentative de connexion échoue je viens de l'appelersocket->close()
et d'en rester là. J'ai quitté ledisconnect
méthode, comme c'était avec le polishutdown
appel.Par souci de clarté, voici la dernière méthode que j'ai utilisé (mais ceci est basé sur bjlaub de réponse, donc prière de fournir toutes les upvotes à lui):
J'ai déclaré la
socket
membre en tant quescoped_ptr
:Puis j'ai modifié mon
connect
méthode:Remarque: cette question a été à l'origine demandé et répondu en 2010, mais si vous êtes maintenant à l'aide de C++11 ou plus, alors
std::unique_ptr
devrait normalement être un meilleur choix queboost::scoped_ptr
std::unique_ptr
au lieu deboost::scoped_ptr
que c'est une meilleure option et C++11 est largement disponible.J'ai fait quelque chose de similaire à l'aide de coup de pouce.Asio dans le passé. J'utilise les méthodes asynchrones, donc, une reconnexion est généralement de laisser mon ip: tcp::socket objet hors de portée, puis en créer un nouveau pour les appels à async_connect. Si async_connect échoue, je utiliser un timer pour dormir un peu, puis réessayez.
socket.close()
lorsque vous détectez une erreur, et puis en créer un nouveau?J'ai essayé les deux, la méthode close() et la méthode d'arrêt et ils sont juste difficile pour moi. Close() peut renvoyer une erreur que vous avez besoin de l'attraper et la bonne façon de faire ce que vous voulez 🙂 et d'arrêt() semble être le meilleur mais sur les logiciels multithread, je trouve que cela peut être difficile. Donc, la meilleure façon est, que Sam a dit, de le laisser aller hors de portée. Si la prise est un membre de la classe, vous pouvez 1) la refonte de sorte que la classe utilise une 'connexion' objet d'envelopper le socket et le laisser aller hors de portée ou 2) de l'envelopper dans un pointeur intelligent et réinitialiser le pointeur intelligent. Si vous utilisez le boost, y compris les shared_ptr est pas cher et fonctionne comme un charme. N'a jamais eu une prise de nettoyer question de le faire avec un shared_ptr. Juste mon expérience.
close(),
"épineux" ou pas. Sinon vous avez une fuite de ressources. Il n'y a rien 'grossier' à ce sujet. Etshutdown()
n'est pas une alternative pourclose()
.Depuis C++11, vous pouvez écrire:
Ci-dessus crée une instance locale de la prise du type de déplacer la construire à partir de socket.
Avant la ligne suivante, sans nom de l'instance locale est détruite, laissant prise dans un "fraîchement construit à partir de io_service" de l'état.
Voir:
https://www.boost.org/doc/libs/1_63_0/doc/html/boost_asio/reference.html#boost_asio.reference.basic_stream_socket.basic_stream_socket.overload5