Domanda

Esiste un modo per annullare un'operazione in sospeso (senza disconnessione) o impostare un timeout per le funzioni della libreria boost?

vale a dire. Voglio impostare un timeout sul blocco socket in boost asio?

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

Esempio: voglio leggerne alcuni dal socket, ma voglio lanciare un errore se sono trascorsi 10 secondi.

È stato utile?

Soluzione

In Linux / BSD il timeout sulle operazioni di I / O sui socket è direttamente supportato dal sistema operativo. L'opzione può essere abilitata tramite setsocktopt () . Non so se boost :: asio fornisce un metodo per impostarlo o espone il socket scriptor per permetterti di impostarlo direttamente - quest'ultimo caso non è realmente portatile.

Per completezza ecco la descrizione dalla pagina man:

  

SO_RCVTIMEO e 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.

Altri suggerimenti

Quando è stata posta questa domanda, immagino che ASIO non avesse alcun esempio su come realizzare ciò di cui aveva bisogno l'OP, ovvero il timeout di un'operazione di blocco come un'operazione di blocco socket. Ora ci sono esempi per mostrarti esattamente come farlo. l'esempio sembra lungo, ma è perché è ben commentato. Illustra come utilizzare ioservice in una modalità "one shot".

Penso che l'esempio sia un'ottima soluzione. Le altre soluzioni qui infrangono la portabilità e non sfruttano ioservice. se la portabilità non è importante e ioservice sembra sovraccarico, quindi-- non dovresti usare ASIO. Indipendentemente da ciò, avrai creato un ioservice (quasi tutte le funzionalità ASIO dipendono da esso, anche sincronizzano i socket), quindi approfittane.

Timeout di un'operazione asio tcp bloccante

Timeout un'operazione di blocco asio udp

La documentazione ASIO è stata aggiornata, quindi dai un'occhiata a nuovi esempi su come superare alcuni dei 'gotchas' che ASIO deve avere.

Potresti fare un async_read e anche impostare un timer per il timeout desiderato. Quindi se il timer si attiva, chiama Annulla sull'oggetto socket. Altrimenti se la lettura avviene, è possibile annullare il timer. Ciò richiede ovviamente l'uso di un oggetto io_service.

modifica: ho trovato uno snippet di codice che lo fa

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

Avevo la stessa domanda e, dopo alcune ricerche, la soluzione più semplice e pulita che potevo trovare era quella di ottenere il socket nativo sottostante e fare una selezione finché non c'erano dati da leggere. Seleziona richiederà un parametro di timeout. Naturalmente, lavorare con il socket nativo inizia ad andare contro il punto di usare asio in primo luogo, ma ancora una volta, questo sembra essere il modo più pulito. Per quanto ne so, asio non fornisce un modo per farlo facilmente per l'uso sincrono. Codice:

        // 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);

        ...

Forse questo sarà utile per la tua situazione.

TL; DR

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

RISPOSTA COMPLETA Questa domanda continua a essere posta più volte per molti anni. Le risposte che ho visto finora sono piuttosto povere. Aggiungerò queste informazioni proprio qui in una delle prime occorrenze di questa domanda.

Tutti coloro che tentano di utilizzare ASIO per semplificare il proprio codice di rete sarebbero perfettamente felici se l'autore aggiungesse semplicemente un timeout di parametro opzionale a tutte le funzioni di sincronizzazione e asincrono. Sfortunatamente, è improbabile che ciò accada (secondo la mia modesta opinione, solo per motivi ideologici, dopo tutto, AS in ASIO è per una ragione).

Quindi questi sono i modi per scuoiare questo povero gatto disponibile finora, nessuno dei quali particolarmente appetitoso. Diciamo che abbiamo bisogno di un timeout di 200ms.

1) API socket vecchio (buono) buono:

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

Nota queste peculiarità: - const int per timeout - su Windows il tipo richiesto è in realtà DWORD, ma per fortuna l'attuale set di compilatori ha lo stesso, quindi const int funzionerà sia nel mondo Win che Posix. - (const char *) per valore. Su Windows const char * è richiesto, Posix richiede const void *, in C ++ const char * verrà convertito in const void * silenziosamente mentre non è vero il contrario.

Vantaggi: funziona e probabilmente funzionerà sempre poiché l'API socket è vecchia e stabile. Abbastanza semplice. Veloce. Svantaggi: tecnicamente potrebbe richiedere file di intestazione appropriati (diversi su Win e persino diversi tipi di UNIX) per setsockopt e le macro, ma l'attuale implementazione di ASIO inquina comunque lo spazio dei nomi globale con loro. Richiede una variabile per il timeout. Non sicuro. Su Windows, è necessario che il socket sia in modalità sovrapposta per funzionare (che fortunatamente utilizza l'attuale implementazione ASIO, ma è comunque un dettaglio di implementazione). BRUTTO!

2) Opzione socket ASIO personalizzata:

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 });

Vantaggi: abbastanza semplice. Veloce. Bello (con typedef). Svantaggi: dipende dai dettagli di implementazione di ASIO, che potrebbero cambiare (ma OTOH alla fine cambierà tutto e tali dettagli hanno meno probabilità di cambiare rispetto alle API pubbliche soggette a standardizzazione). Ma in questo caso, dovrai scrivere una classe in base a https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SectableSocketOption.html (che è ovviamente un grande PITA grazie all'ovvio sovrastimamento di questa parte di ASIO) o meglio ancora tornare a 1.

3) Usa le funzionalità asincrone / future di C ++.

#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;
    }

Vantaggi: standard. Svantaggi: avvia sempre un nuovo thread (in pratica), che è relativamente lento (potrebbe essere abbastanza buono per i client, ma porterà alla vulnerabilità DoS per i server poiché thread e socket sono "costosi" risorse). Non provare a usare std :: launch :: deferred invece di std :: launch :: async per evitare il lancio di nuovi thread poiché wait_for restituirà sempre future_status :: deferred senza provare a eseguire il codice.

4) Il metodo prescritto da ASIO - usa solo operazioni asincrone (che non è proprio la risposta alla domanda).

Vantaggi: abbastanza buono anche per i server se non è richiesta un'enorme scalabilità per transazioni brevi. Svantaggi: abbastanza prolisso (quindi non includerò nemmeno esempi - vedi esempi ASIO). Richiede una gestione a vita molto attenta di tutti i tuoi oggetti utilizzati sia dalle operazioni asincrone sia dai loro gestori di completamento, che in pratica richiede che tutte le classi contenenti e utilizzando tali dati nelle operazioni asincrone siano derivate da enable_shared_from_this, che richiede tutte tali classi allocate su heap, il che significa ( almeno per le operazioni brevi) che la scalabilità inizierà a diminuire dopo circa 16 thread poiché ogni allocazione / dealloc dell'heap utilizzerà una barriera di memoria.

In seguito a ciò che ha detto grepsedawk. Ci sono alcuni esempi che mostrano come annullare le operazioni asincrone a esecuzione prolungata dopo un periodo di tempo, nella sezione Timeout all'interno di asio doco. Potenzia gli esempi di Asio . Il client TCP Async mi ha aiutato di più.

Happy Asyncing :)

Anche anni dopo la domanda originale, non c'è ancora una risposta soddisfacente.

L'uso manuale di select non è una buona opzione

  1. il numero del descrittore di file deve essere inferiore a 1024
  2. FD può essere spuriamente segnalato come pronto a causa di un checksum errato.

Chiamare io_service.run_one () è anche una cattiva idea, perché potrebbero esserci altre opzioni asincrone che richiedono un io_service per eseguire sempre run () . E il documento di boost sul blocco del client tcp è difficile da comprendere.

Quindi ecco la mia soluzione. L'idea chiave è la seguente:

{
    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);
}

Qui Semaphore è solo un mutex e una condizione_variabile.
wait_for è implementato da http: //en.cppreference .com / w / cpp / thread / condition_variable / wait_for

Il codice completo è all'indirizzo https://github.com/scinart /cpplib/blob/master/include/asio.hpp
Esempi sono in https://github.com/scinart/ cpplib / blob / master / test / test_asio.cpp
Esempio migliore su https://github.com/scinart/cpplib/blob /master/test/test_SyncBoostIO.cpp

Puoi avvolgere le chiamate sincrone in future e attendere che si completi con un timeout (wait_timeout).

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

Certamente non una taglia va bene per tutti, ma funziona bene per es. aggirare i timeout di connessione lenta.

Su * nix, useresti alarm () in modo che la tua chiamata socket fallisca con EINTR

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top