Question

Existe-t-il un moyen d’annuler une opération en attente (sans déconnexion) ou de définir un délai d’expiration pour les fonctions de la bibliothèque boost?

I.e. Je souhaite définir un délai d’expiration pour le blocage de la prise en boost asio?

socket.read_some (boost :: asio :: buffer (pData, maxSize), erreur _);

Exemple: je veux lire des extraits du socket, mais je veux générer une erreur si 10 secondes se sont écoulées.

Était-ce utile?

La solution

Sous Linux / BSD, le délai d’expiration des opérations d’E / S sur les sockets est directement pris en charge par le système d’exploitation. L'option peut être activée via setsocktopt () . Je ne sais pas si boost :: asio fournit une méthode pour le configurer ou expose le script script de socket pour vous permettre de le définir directement - ce dernier cas n'est pas vraiment portable.

Par souci d'exhaustivité, voici la description de la page de manuel:

  

SO_RCVTIMEO et SO_SNDTIMEO

          Specify the receiving or sending  timeouts  until  reporting  an
          error.  The argument is a struct timeval.  If an input or output
          function blocks for this period of time, and data has been  sent
          or  received,  the  return  value  of  that function will be the
          amount of data transferred; if no data has been transferred  and
          the  timeout has been reached then -1 is returned with errno set
          to EAGAIN or EWOULDBLOCK just as if the socket was specified  to
          be  non-blocking.   If  the timeout is set to zero (the default)
          then the operation  will  never  timeout.   Timeouts  only  have
          effect  for system calls that perform socket I/O (e.g., read(2),
          recvmsg(2), send(2), sendmsg(2)); timeouts have  no  effect  for
          select(2), poll(2), epoll_wait(2), etc.

Autres conseils

Lorsque cette question a été posée, je suppose que ASIO n’avait aucun exemple de la manière dont il fallait accomplir le PO, c’est-à-dire expirer une opération de blocage telle qu’une opération de socket bloquante. Maintenant, il existe des exemples pour vous montrer exactement comment faire cela. l’exemple semble long, mais c’est parce qu’il est WELL commenté. Il montre comment utiliser ioservice dans un type de mode "one shot".

Je pense que l'exemple est une excellente solution. Les autres solutions proposées ici casser la portabilité et ne tirent pas parti de ioservice. si la portabilité n'est pas importante et que ioservice semble trop lourd - THEN--, vous ne devriez pas utiliser ASIO. Quoi qu’il en soit, vous aurez un ioservice créé (presque toutes les fonctionnalités ASIO en dépendent, même les sockets synchronisés), profitez-en.

Délai d'attente d'une opération de blocage asio tcp

Interruption du délai d'attente d'une opération asio udp

La documentation ASIO a été mise à jour, alors jetez un œil à de nouveaux exemples sur la façon de surmonter certains des pièges qu'utilisait ASIO.

Vous pouvez faire un async_read et également définir une minuterie pour le délai d'expiration souhaité. Ensuite, si la minuterie se déclenche, appelez cancel sur votre objet socket. Sinon, si votre lecture se produit, vous pouvez annuler votre minuterie. Cela nécessite bien sûr que vous utilisiez un objet io_service.

modifier: vous avez trouvé un extrait de code qui le fait

http://lists.boost.org/Archives/boost/ 2007/04 / 120339.php

J'avais la même question et après quelques recherches, la solution la plus simple et la plus propre que je pouvais trouver était d'obtenir le socket natif sous-jacent et d'effectuer une sélection jusqu'à ce qu'il y ait des données à lire. Select prendra un paramètre de délai d'attente. Bien sûr, travailler avec le socket natif commence à aller à l’encontre du but d’utiliser asio en premier lieu, mais là encore, cela semble être le moyen le plus propre. Autant que je sache, asio ne fournit pas un moyen de le faire facilement pour une utilisation synchrone. Code:

        // socket here is:  boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr

        // Set up a timed select call, so we can handle timeout cases.

        fd_set fileDescriptorSet;
        struct timeval timeStruct;

        // set the timeout to 30 seconds
        timeStruct.tv_sec = 30;
        timeStruct.tv_usec = 0;
        FD_ZERO(&fileDescriptorSet);

        // We'll need to get the underlying native socket for this select call, in order
        // to add a simple timeout on the read:

        int nativeSocket = a_socket_ptr->native();

        FD_SET(nativeSocket,&fileDescriptorSet);

        select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

        if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout

                std::string sMsg("TIMEOUT on read client data. Client IP: ");

                sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());

                throw MyException(sMsg);
        }

        // now we know there's something to read, so read
        boost::system::error_code error;
        size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);

        ...

Cela sera peut-être utile dans votre cas.

TL; DR

socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });

Réponse complète Cette question continue à être posée maintes et maintes fois pendant de nombreuses années. Les réponses que j'ai vues jusqu'à présent sont assez pauvres. J'ajouterai cette information ici, dans l'une des premières occurrences de cette question.

Tous ceux qui essaient d’utiliser ASIO pour simplifier leur code réseau seraient tout à fait heureux si l’auteur ajoutait simplement un paramètre de délai d’expiration facultatif à toutes les fonctions de synchronisation et async io. Malheureusement, il est peu probable que cela se produise (à mon humble avis, pour des raisons idéologiques, après tout, AS in ASIO l’est pour une raison).

Ce sont donc les moyens de dépecer ce pauvre chat disponible jusqu’à présent, aucun d’entre eux n’étant particulièrement appétissant. Disons que nous avons besoin d’un délai d’attente de 200 ms.

1) Bonne (mauvaise) vieille API de socket:

const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops

Veuillez noter ces particularités: - const int pour timeout - sous Windows, le type requis est en réalité DWORD, mais les compilateurs actuels l'ont heureusement identique, de sorte que const int fonctionnera à la fois dans le monde Win et Posix. - (const char *) pour la valeur. Sous Windows, const car * requis, Posix requiert const void *, en C ++, const car * sera converti en const void * en silence alors que l'inverse n'est pas vrai.

Avantages: fonctionne et fonctionnera probablement toujours car l’API de socket est ancienne et stable. Assez simple. Vite. Inconvénients: techniquement, des ensembles d’en-têtes appropriés (différents sur Win et même sous UNIX) peuvent être nécessaires pour setsockopt et les macros, mais la mise en oeuvre actuelle d’ASIO pollue néanmoins l’espace de noms global. Nécessite une variable pour le délai d'attente. Pas de type-safe. Sous Windows, nécessite que le socket soit en mode superposé pour fonctionner (que l'implémentation ASIO actuelle utilise heureusement, mais il s'agit toujours d'un détail d'implémentation). Moche!

2) Option de socket ASIO personnalisée:

typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });

Avantages: Assez simple. Vite. Beau (avec typedef). Inconvénients: Dépend des détails de l'implémentation ASIO, ce qui pourrait changer (mais OTOH va tout changer à la longue, et ces détails sont moins susceptibles de changer que les API publiques soumises à la normalisation). Mais au cas où cela se produirait, vous devrez soit écrire une classe selon https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (ce qui est bien sûr un important PITA grâce à une ingénierie excessive cette partie de ASIO) ou mieux encore revenir à 1.

3) Utilisez les installations C ++ asynchrones / futures.

#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
    .wait_for(std::chrono::milliseconds{ 200 });
switch (status)
    {
    case std::future_status::deferred:
    //... should never happen with std::launch::async
        break;
    case std::future_status::ready:
    //...
        break;
    case std::future_status::timeout:
    //...
        break;
    }

Avantages: standard. Inconvénients: démarre toujours un nouveau thread (en pratique), ce qui est relativement lent (cela peut suffire aux clients, mais peut conduire à une vulnérabilité DoS pour les serveurs car les threads et les sockets sont des ressources "coûteuses"). N'essayez pas d'utiliser std :: launch :: deferred au lieu de std :: launch :: async pour éviter le lancement d'un nouveau thread, wait_for retournera toujours future_status :: deferred sans essayer d'exécuter le code.

4) La méthode prescrite par ASIO - utilisez uniquement des opérations asynchrones (ce qui n’est pas vraiment la réponse à la question).

Avantages: convient également aux serveurs si une énorme évolutivité pour les transactions courtes n’est pas requise. Inconvénients: assez verbeux (je n'inclurai donc pas d'exemples - voir les exemples ASIO). Requiert une gestion très minutieuse de la durée de vie de tous vos objets utilisés à la fois par des opérations asynchrones et par leurs gestionnaires d'achèvement. En pratique, toutes les classes contenant et utilisant de telles données dans des opérations asynchrones doivent être dérivées de enable_shared_from_this, ce qui nécessite que toutes ces classes soient allouées sur heap, ce qui signifie ( au moins pour les opérations courtes), l’évolutivité va commencer à s’effacer après environ 16 threads, chaque tas alloué / dealloc utilisant une barrière de mémoire.

Nous allons maintenant à ce que grepsedawk a mentionné. Quelques exemples montrent comment annuler des opérations asynchrones de longue durée après une certaine période, dans la section Délais du document asio doco. Exemples Boost Asio . Un client TCP asynchrone m'a le plus aidé.

Happy Asyncing:)

Même des années après la question initiale, la réponse n’est toujours pas satisfaisante.

Utiliser manuellement select n'est pas une bonne option

  1. le numéro du descripteur de fichier doit être inférieur à 1024
  2. FD peut être faussement signalé comme étant prêt en raison d'une somme de contrôle incorrecte.

Appeler io_service.run_one () est également une mauvaise idée car d'autres options asynchrones peuvent nécessiter un service io_service pour toujours run () . Et le document de boost sur le blocage du client TCP est difficile à comprendre.

Alors voici ma solution. L'idée clé est la suivante:

{
    Semaphore r_sem;
    boost::system::error_code r_ec;
    boost::asio::async_read(s,buffer,
                            [this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
                                r_ec=ec_;
                                r_sem.notify();
                            });
    if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
    {
        s.cancel();
        r_sem.wait();
        throw boost::system::system_error(boost::asio::error::try_again);
    }
    else if(r_ec)
        throw boost::system::system_error(r_ec);
}

Ici, Semaphore est simplement un mutex et une variable de condition.
wait_for est implémenté par http: //en.cppreference. .com / w / cpp / thread / variable_condition / wait_for

Le code complet est disponible à l'adresse https://github.com/scinart. /cpplib/blob/master/include/asio.hpp
Des exemples sont dans https://github.com/scinart/ cpplib / blob / master / test / test_asio.cpp
Meilleur exemple à https://github.com/scinart/cpplib/blob /master/test/test_SyncBoostIO.cpp

Vous pouvez intégrer les appels synchrones dans des contrats à terme et attendre qu'ils se terminent avec un délai d'attente (wait_timeout).

http: // www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.futures

Certainement, pas une taille unique, mais cela fonctionne bien, par exemple. contournant les délais de connexion lents.

Sur * nix, vous utiliseriez alarm () pour que votre appel échoue avec EINTR

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top