2008-11-15 10 views
23

Gibt es eine Möglichkeit, eine ausstehende Operation abzubrechen (ohne die Verbindung abzubrechen) oder eine Zeitüberschreitung für die Funktionen der Boost-Bibliothek festzulegen?Wie setze ich ein Timeout beim Blockieren von Sockets in Boost Asio?

I.e. Ich möchte ein Timeout beim Blockieren Socket in Boost Asio setzen?

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

Beispiel: Ich möchte etwas vom Socket lesen, aber ich möchte einen Fehler ausgeben, wenn 10 Sekunden vergangen sind.

Antwort

8

Unter Linux/BSD das Timeout auf I/O-Operationen auf Buchsen direkt vom Betriebssystem unterstützt wird. Die Option kann über setsocktopt() aktiviert werden. Ich weiß nicht, ob boost::asio eine Methode zur Verfügung stellt oder legt den Socket-Skript offen, damit Sie es direkt einstellen können - der letztere Fall ist nicht wirklich tragbar.

Für einen der Vollständigkeit halber hier die Beschreibung des Mannes Seite:

SO_RCVTIMEO und 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. 
+1

Dies wäre eine großartige Lösung, aber sie haben keine solchen Socket-Optionen. Siehe Socket-Optionen hier: http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/reference.html –

+0

Boost sind gut, aber nicht perfekt :-) –

+4

Aber was ich sagen kann, asios read_some() wird weiterhin intern für immer fortgesetzt, wenn es nichts liest, wodurch der Effekt von SO_RCVTIMEO aufgehoben wird, den Sie möglicherweise für den systemeigenen Socket gesetzt haben. Sieht so aus, als würde select() mit socket.native() immer noch der naheliegendste Weg sein, ein Timeout zu haben. –

-2

auf * nix, du Alarm verwenden würde() so Ihre Socket-Aufruf würde mit EINTR scheitern

+0

nicht alle Anrufe Ihrer Steckdose Würde, mit EINTR in alle Fäden scheitern? Das klingt schlecht. –

9

Sie eine async_read tun könnte und stellen auch einen Timer für Ihre gewünschte Auszeit. Wenn der Timer ausgelöst wird, rufen Sie cancel für Ihr Socket-Objekt auf. Andernfalls können Sie Ihren Timer abbrechen, wenn Ihr Lesevorgang ausgeführt wird. Dazu müssen Sie natürlich ein io_service-Objekt verwenden.

edit: Gefunden ein Code-Snippet für Sie, das dies tut

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

+1

Dieses Snippet enthält einen Aufruf von 'io_service :: reset()'. Aber die Dokumentation dafür sagt 'Diese Funktion darf nicht aufgerufen werden, während unfertige Aufrufe an die Funktionen run(), run_one(), poll() oder poll_one() erfolgen. – Gabriel

+0

Wenn dieser Code in einem asio async ausgeführt werden soll Callback-Ereignis, das heißt, in etwas, das von 'io_service :: run' aufgerufen wird, vermute ich, dass Sie undefiniertes Verhalten erhalten. – Gabriel

6

ich die gleiche Frage hatte, und nach einigen Recherchen, die einfachste und sauberste Lösung, die ich tun konnte, war das darunter liegende zu bekommen nativen Socket, und führen Sie eine Auswahl durch, bis Daten zu lesen waren. Wählen Sie einen Timeout-Parameter. Natürlich beginnt die Arbeit mit dem nativen Socket gegen den Punkt der Verwendung von Asio an erster Stelle, aber wieder scheint dies der sauberste Weg zu sein. Soweit ich das beurteilen konnte, bietet asio keine Möglichkeit, dies für die synchrone Nutzung leicht zu tun. 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); 

     ... 

Vielleicht wird dies für Ihre Situation nützlich sein.

2

Im Anschluss an, was grepreseawk erwähnt hat. Es gibt ein paar Beispiele, wie lang andauernde asynchrone Operationen nach einer gewissen Zeit unter Timeouts innerhalb von asio doco abgebrochen werden können. Boost Asio Examples .Async TCP client helped me the most.

Glückliche Asyncing :)

13

Wenn diese Frage gestellt wurde, ich denke, ASIO hat kein Beispiel dafür, wie zu erreichen, was die OP benötigt, dh eine Sperroperation wie ein Sperrbuchse Betrieb Timeout. Jetzt gibt es Beispiele, die Ihnen genau zeigen, wie Sie das tun. Das Beispiel scheint lange, aber das ist, weil es gut kommentiert wird. Es zeigt, wie man den ioservice in einem "One-Shot" -Modus benutzt.

Ich denke, das Beispiel ist eine großartige Lösung. Die anderen Lösungen unterbrechen die Portabilität und nutzen ioservice nicht. Wenn Portabilität nicht wichtig ist und der Ioservice zu viel Overhead scheint - THEN-- sollten Sie ASIO nicht verwenden. Egal was, Sie werden einen ioservice erstellen lassen (fast alle ASIO-Funktionen hängen davon ab, sogar Sync-Sockets), also nutzen Sie ihn aus.

Timeout a blocking asio tcp operation

Timeout a blocking asio udp operation

Die ASIO-Dokumentation wurde aktualisiert, so dass es Check-Out für neue Beispiele, wie man haben einige der 'gotchas' ASIO zu überwinden verwenden.

+0

Unter Windows blockiert 'io_service.run_one()' niemals async_read (zumindest in Boost 1.59.0), was zu einer 100% igen CPU-Auslastung führt. – rustyx

+3

Die Beispiele sind lang, weil die Dokumentation 3x redundant ist. Ich halte das nicht für "gut kommentiert". – ChristophK

1

Auch Jahre nach der ursprünglichen Frage gibt es immer noch keine befriedigende Antwort.

manuell auswählen verwenden, ist keine gute Option

  1. Dateideskriptors Zahl sein muss weniger als 1024
  2. FD spuriously als bereit aufgrund falscher Prüfsumme gemeldet werden kann.

Anruf io_service.run_one() ist auch eine schlechte Idee, weil es andere Asynchron-Optionen sein kann, die eine io_service run() immer benötigt. Und Boosts Dokument über das Blockieren von TCP-Client ist schwer zu verstehen.

Also hier ist meine Lösung. Die Schlüsselidee ist folgende:

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

Hier Semaphore ist nur ein Mutex und ein condition_variable.
wait_for von http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

Voll Code implementiert ist, ist an ist https://github.com/scinart/cpplib/blob/master/include/asio.hpp
Beispiele in https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp
besseres Beispiel bei https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp