Comment définir un temps d'arrêt sur les sockets de blocage dans boost asio?

y a-t-il un moyen d'annuler une opération en attente (sans déconnexion) ou de définir un délai pour les fonctions de boost library?

<!-C'est-à-dire: Je veux mettre un temps d'arrêt sur le bloc socket dans boost asio?

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

Exemple: je veux lire certains de la prise, mais je veux lever une erreur si 10 secondes se sont écoulées.

24
demandé sur Brian R. Bondy 2008-11-15 03:31:22

9 réponses

sous Linux / BSD, Le timeout des opérations d'E/S sur les sockets est directement supporté par le système d'exploitation. L'option peut être activée à l'aide de setsocktopt(). Je ne sais pas si boost::asio fournit une méthode pour le mettre ou expose la prise de script pour vous permettre de régler directement, ce dernier cas n'est pas vraiment portable.

pour être complet 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.
8
répondu Nicola Bonelli 2008-11-15 12:31:21

lorsque cette question a été posée, je suppose QU'ASIO n'avait pas d'exemple sur la façon d'accomplir ce dont L'OP avait besoin, c'est-à-dire de temporiser une opération de blocage telle qu'une opération de blocage de socket. Maintenant, il existe des exemples pour vous montrer exactement comment le faire. l'exemple semble long, mais c'est parce qu'il est BIEN commenté. Il montre comment utiliser le ioservice en mode "one shot".

je pense que l'exemple est une excellente solution. Les autres solutions ici cassent la portabilité et ne profitez de ioservice. si la portabilité n'est pas importante et que l'ioservice semble avoir beaucoup de frais généraux, alors vous ne devriez pas utiliser ASIO. Quoi qu'il en soit, vous aurez un ioservice créé (presque toutes les fonctionnalités D'ASIO en dépendent, même les sockets de synchronisation) donc, profitez-en.

Délai d'attente d'un blocage asio tcp opération

Délai d'attente d'un blocage asio udp opération

la documentation ASIO a été mise à jour, donc vérifiez pour de nouveaux exemples sur la façon de surmonter certains des 'pièges' ASIO utiliser pour avoir.

14
répondu William Symionow 2012-12-28 02:57:56

vous pouvez faire un async_read et aussi définir un timer pour votre temps d'arrêt désiré. Ensuite, si la minuterie s'allume, appelez cancel sur votre objet socket. Sinon, si votre lecture se produit, vous pouvez annuler votre minuterie. Cela nécessite l'utilisation d'un objet io_service bien sûr.

edit: Trouvé un extrait de code pour vous que ce n'

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

9
répondu grepsedawk 2008-11-26 02:41:24

j'ai eu la même question, et après quelques recherches, la solution la plus simple et la plus propre que j'ai pu trouver était d'obtenir le socket natif sous-jacent, et de faire un select jusqu'à ce qu'il y ait des données à lire. Select va prendre un paramètre timeout. Bien sûr, travailler avec le socket natif commence à aller à l'encontre du point d'utilisation d'asio en premier lieu, mais encore une fois, cela semble être la voie la plus propre. Pour autant que je puisse dire, asio ne fournit pas un moyen de le faire pour l'usage synchrone facilement. 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);

        ...

peut-être cela sera utile pour votre situation.

7
répondu Ty Hoffman 2011-04-20 18:43:43

faisant suite à ce que grepsedawk a mentionné. Il y a quelques exemples qui montrent comment annuler des opérations asynchrones de longue durée après un certain temps, sous le délais d'attenteBoost Asio Examples . async TCP client m'a le plus aidé.

Heureux Asyncing :)

2
répondu denn 2012-01-08 23:56:40

même des années après la question initiale, il n'y a toujours pas de réponse satisfaisante.

L'utilisation manuelle de select n'est pas une bonne option

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

Appel io_service.run_one() est aussi une mauvaise idée, car il peut y avoir d'autres options async qui ont besoin d'un io_service pour toujours run(). Et le document de boost sur le blocage du client tcp est difficile à comprendre.

voici donc 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 juste un mutex et une condition_variable.

wait_for est mis en œuvre par http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

le code Complet est à https://github.com/scinart/cpplib/blob/master/include/asio.hpp

des Exemples est dans https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp

Le meilleur Exemple à https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp

1
répondu scinart 2018-03-06 04:54:21

TL;DR

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

RÉPONSE COMPLÈTE Cette question est posée encore et encore pendant de nombreuses années. Les réponses que j'ai vues jusqu'à présent sont assez pauvres. Je vais ajouter cette info ici, dans l'une des premières occurrences de cette question.

toute personne essayant D'utiliser ASIO pour simplifier son code réseau serait parfaitement heureuse si l'auteur ajoutait simplement un paramètre optionnel timeout à toutes les fonctions de synchronisation et d'asynchrone. Malheureusement, il est peu probable que cela se produise (dans mon humble l'opinion, juste pour des raisons idéologiques, après tout, comme dans ASIO est pour une raison).

voici donc les moyens de dépouiller ce pauvre chat disponible jusqu'à présent, aucun d'eux n'est particulièrement appétissant. Disons qu'il nous faut 200ms de temps mort.

1) Bonne (Mauvaise) ancienne 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-sur Windows le type requis est en fait DWORD, mais l'ensemble actuel de compilateurs a heureusement la même chose, donc const int fonctionnera à la fois dans Gagner et Posix monde. - (const char*) pour la valeur. Sur Windows const char* est nécessaire, Posix nécessite const void* en C++ const char* convertir à const void* silencieusement tandis que l'inverse n'est pas vrai.

avantages: fonctionne et fonctionnera probablement toujours car l'API socket est ancienne et stable. Assez Simple. Rapide. Inconvénients: techniquement pourrait exiger des fichiers d'en-tête appropriés (différents sur Win et même différentes saveurs UNIX) pour setsockopt et les macros, mais l'implémentation actuelle de toute façon, ASIO pollue l'Espace-nom mondial avec eux. Nécessite une variable pour timeout. Pas de type sécurisé. Sur Windows, nécessite que la socket soit en mode superposé pour fonctionner (ce que L'implémentation actuelle D'ASIO utilise heureusement, mais c'est toujours un détail d'implémentation). LAID!

2) Personnalisé ASIO option de socket:

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. Rapide. Belle (avec typedef). Inconvénients: dépend de détails de mise en œuvre ASIO, qui pourrait changer (mais OTOH tout sera il est peu probable que de tels détails changent, alors que les IPA publics peuvent faire l'objet d'une normalisation). Mais au cas où cela se produirait, vous devrez soit écrire un cours selon https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (qui est bien sûr une PITA majeure grâce à la sur-Ingénierie évidente de cette partie D'ASIO) ou mieux encore revenir à 1.

3) Utiliser C++ async / future installation.

#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: lance toujours un nouveau thread (dans la pratique), qui est relativement lent (pourrait être assez bon pour les clients, mais conduira à la vulnérabilité de DoS pour les serveurs comme les threads et les sockets sont des ressources "coûteuses"). N'essayez pas d'utiliser std::lancement::reporté au lieu de std::lancement::asynchrone pour éviter de nouveau thread lancement wait_for retournera toujours future_status::différé sans essayer d'exécuter le code.

4) La méthode prescrit par ASIO-utiliser des opérations asynchrones seulement (ce qui n'est pas vraiment la réponse à la question).

avantages: assez bon pour les serveurs aussi si l'extensibilité énorme pour les transactions courtes n'est pas nécessaire. Inconvénients: assez verbeux (donc je ne vais même pas inclure d'exemples - voir les exemples D'ASIO). Nécessite une gestion très soigneuse de tous vos objets utilisés par les opérations asynchrones et leurs manipulateurs de complétion, ce qui nécessite en pratique toutes les classes contenant et utilisant de telles données les opérations async sont dérivées de enable_shared_from_this, qui nécessite toutes les classes de ce type allouées sur heap, ce qui signifie (au moins pour les opérations courtes) que l'évolutivité commencera à diminuer après environ 16 threads car chaque alloc/dealloc de heap utilisera une barrière de mémoire.

0
répondu Pavel Verevkin 2018-08-14 21:29:01

vous pouvez envelopper les appels synchrones dans des contrats à terme et attendre qu'ils soient complétés par un timeout (wait_timeout).

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

certainement pas une taille unique convient à tous, mais fonctionne bien pour par exemple contourner les délais de connexion lente.

-1
répondu hplbsh 2011-09-16 17:47:47

Sur *nix, il vous suffit d'utiliser l'alarme (le) de sorte que votre prise d'appel échoue avec EINTR

-2
répondu Paul Betts 2008-11-15 05:11:08