boost:: asio déconnecter proprement
Parfois boost:: asio semble se déconnecter avant que je le veuille, c'est-à-dire avant que le serveur ne gère correctement la déconnexion. Je ne suis pas sûr de savoir comment cela est possible parce que le client semble penser qu'il a complètement Envoyé le message, mais lorsque le serveur émet l'erreur, il ne lit même pas l'en-tête du message... Pendant le test, cela ne se produit que 1 fois sur 5, le serveur reçoit le message d'arrêt du client et déconnecte le client proprement.
L'erreur: "une connexion existante a été forcée fermée par l'hôte distant"
La déconnexion du client:
void disconnect()
{
boost::system::error_code error;
//just creates a simple buffer with a shutdown header
boost::uint8_t *packet = createPacket(PC_SHUTDOWN,0);
//sends it
if(!sendBlocking(socket,packet,&error))
{
//didnt get here in my tests, so its not that the write failed...
logWrite(LOG_ERROR,"server",
std::string("Error sending shutdown message.n")
+ boost::system::system_error(error).what());
}
//actaully disconnect
socket.close();
ioService.stop();
}
bool sendBlocking(boost::asio::ip::tcp::socket &socket,
boost::uint8_t *data, boost::system::error_code* error)
{
//get the length section from the message
boost::uint16_t len = *(boost::uint16_t*)(data - 3);
//send it
asio::write(socket, asio::buffer(data-3,len+3),
asio::transfer_all(), *error);
deletePacket(data);
return !(*error);
}
Le serveur:
void Client::clientShutdown()
{
//not getting here in problem cases
disconnect();
}
void Client::packetHandler(boost::uint8_t type, boost::uint8_t *data,
boost::uint16_t len, const boost::system::error_code& error)
{
if(error)
{
//error handled here
delete[] data;
std::stringstream ss;
ss << "Error recieving packet.n";
ss << logInfo() << "n";
ss << "Error: " << boost::system::system_error(error).what();
logWrite(LOG_ERROR,"Client",ss.str());
disconnect();
}
else
{
//call handlers based on type, most will then call startRead when
//done to get the next packet. Note however, that clientShutdown
//does not
...
}
}
void startRead(boost::asio::ip::tcp::socket &socket, PacketHandler handler)
{
boost::uint8_t *header = new boost::uint8_t[3];
boost::asio::async_read(socket,boost::asio::buffer(header,3),
boost::bind(&handleReadHeader,&socket,handler,header,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket, PacketHandler handler,
boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
if(error)
{
//error "thrown" here, len always = 0 in problem cases...
delete[] header;
handler(0,0,0,error);
}
else
{
assert(len == 3);
boost::uint16_t payLoadLen = *((boost::uint16_t*)(header + 0));
boost::uint8_t type = *((boost::uint8_t*) (header + 2));
delete[] header;
boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];
boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
boost::bind(&handleReadBody,socket,handler,
type,payLoad,payLoadLen,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
}
void handleReadBody(ip::tcp::socket *socket, PacketHandler handler,
boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
size_t readLen, const boost::system::error_code& error)
{
if(error)
{
delete[] payLoad;
handler(0,0,0,error);
}
else
{
assert(len == readLen);
handler(type,payLoad,len,error);
//delete[] payLoad;
}
}
5 réponses
Je pense que vous devriez probablement avoir un appel à l' socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec)
là avant l'appel de socket.close()
.
La documentation boost::asio pour basic_stream_socket:: close états:
Pour un comportement portable en ce qui concerne la fermeture gracieuse d'une socket connectée, appelez shutdown() avant de fermer la socket.
Cela devrait garantir que toutes les opérations en attente sur le socket sont correctement annulées et que tous les tampons sont vidés avant l'appel au socket.proche.
J'ai essayé de le faire avec la méthode close() et la méthode shutdown ()
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec)
La méthode d'arrêt est la meilleure des deux. Cependant, je trouve que l'utilisation du destructeur du socket ASIO est la façon propre de le faire car ASIO s'occupe de tout pour vous. Donc, votre objectif est de simplement laisser la prise tomber hors de portée. Maintenant, vous pouvez le faire facilement en utilisant un shared_ptr et en réinitialisant le shared_ptr à une nouvelle socket ou null. cela appellera le destructeur de la prise ASIO et la vie est Bien.
Peut-être que c'est ce qui se passe:
- le client envoie le paquet de déconnexion
- le Client arrête le socket
- le gestionnaire de lecture du serveur est appelé, mais une erreur est associée au paquet shutdown car le socket est déjà fermé.
Je vois dans vos gestionnaires de lecture, s'il y a une erreur, vous ne vérifiez jamais si votre paquet d'arrêt est là. Peut-être qu'il est. Fondamentalement ce que je dis est peut être que votre client est parfois capable d'Envoyer à la fois la fermeture et le paquet d'arrêt avant que le serveur ait une chance de les traiter séparément.
Utilisez async_write () et put socket.fermer() à l'intérieur du gestionnaire d'écriture. Cela permettra de s'assurer que le paquet est traité par boost asio et non négligé au milieu du traitement (à cause des appels close ()).
J'ai un problème similaire. Je crois que c'est lié aux connexions de recyclage de Windows. Est la suivante familier?
- vous obtenez cette erreur immédiatement au démarrage du programme mais pas après l'établissement d'une connexion?
- l'erreur ne se produit jamais si vous attendez plus de 4 minutes avant de redémarrer votre application?
Les spécifications tcp spécifient que par défaut, il doit attendre quatre minutes pour l'accusé de réception final lorsqu'une connexion tcp est fermée. Vous pouvez voir ces connexions dans L'état FIN_WAIT en utilisant netstat. Le système D'exploitation Windows détecte lorsque vous essayez de vous connecter exactement au même système et prend ces connexions partiellement fermées et les recycle. Votre deuxième invocation du programme obtient la connexion 'fermée' laissée par la première exécution. Il obtient la prochaine reconnaissance et se ferme vraiment.