2008-11-15 12 views
23

Czy istnieje sposób na anulowanie oczekującej operacji (bez rozłączania) lub ustawienie limitu czasu dla funkcji biblioteki boost?Jak ustawić limit czasu na blokowanie gniazd w asiu?

tj. Chcę ustawić limit czasu na blokowanie gniazda w boost asio?

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

Przykład: chcę odczytać niektóre z gniazd, ale chcę rzucić błąd, jeśli minęło 10 sekund.

Odpowiedz

8

pod Linux/BSD limit czasu na operacje wejścia/wyjścia na gniazdach są bezpośrednio obsługiwane przez system operacyjny. Opcję można włączyć przez setsocktopt(). Nie wiem, czy boost::asio udostępnia metodę ustawienia lub odsłonięcia skryptora gniazda, aby umożliwić użytkownikowi bezpośrednie ustawienie - ten drugi przypadek nie jest naprawdę przenośny.

Przez wzgląd na kompletność Oto opis ze strony człowieka:

SO_RCVTIMEO i 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

To byłoby świetne rozwiązanie, ale nie ma takich opcji gniazd. Zobacz opcje gniazd tutaj: http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/reference.html –

+0

Zwiększenie jest dobre, ale nie doskonałe :-) –

+4

Ale z tego, co mogę powiedzieć, asio's read_some() będzie wewnętrznie kontynuował pętlę na zawsze, jeśli nic nie przeczyta, tym samym anulując działanie SO_RCVTIMEO, które mógłbyś ustawić na macierzystym gnieździe. Wygląda to jak użycie select() z socket.native() nadal będzie najbardziej oczywistym sposobem na przekroczenie limitu czasu. –

-2

W * nix, można użyć alarmu(), więc rozmowa gniazdo zawiedzie z EINTR

+0

Czy nie wszystkie rozmowy gniazdowe, we wszystkich wątkach niepowodzeniem z EINTR? To brzmi źle. –

9

Można zrobić async_read a także ustawić timer dla żądany limit czasu. Następnie, jeśli timer się uruchomi, wywołaj cancel na twoim obiekcie gniazda. W przeciwnym razie, jeśli twój odczyt się stanie, możesz anulować timer. To wymaga oczywiście użycia obiektu io_service.

edit: Znaleziono fragment kodu dla ciebie, że robi to

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

+1

Ten fragment zawiera wywołanie funkcji 'io_service :: reset()'. Ale dokumentacja dla niego mówi: "Ta funkcja nie może być wywołana, gdy są jakieś niedokończone wywołania funkcji run(), run_one(), poll() lub poll_one()." – Gabriel

+0

Jeśli ten kod ma być uruchamiany w asynchronicznej asynchronizacji wywołanie zwrotne, to jest wewnątrz czegoś wywoływanego przez 'io_service :: run', podejrzewam, że otrzymujesz niezdefiniowane zachowanie. – Gabriel

6

Miałem to samo pytanie, a po niektórych badań, najprostszy, najczystszym rozwiązanie mogłem wymyślić było dostać instrumentu bazowego natywnego gniazda i dokonaj wyboru, dopóki nie będzie danych do odczytania. Opcja Wybierz będzie mieć parametr limitu czasu. Oczywiście, praca z natywnym gniazdem zaczyna się w pierwszym rzędzie sprzeczna z używaniem asio, ale znowu wydaje się być najczystszym sposobem. O ile mogłem powiedzieć, asio nie zapewnia tego w łatwy sposób do synchronicznego użycia. Kod:

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

     ... 

Być może będzie to przydatne w twojej sytuacji.

13

Gdy padło pytanie, myślę ASIO nie ma żadnego przykładu, w jaki sposób osiągnąć to, co PO potrzebna, to limit czasu operacji blokowania, takie jak blokowanie operacji gniazda. Teraz istnieją przykłady, aby pokazać dokładnie, jak to zrobić. przykład wydaje się długi, ale to dlatego, że jest WELL skomentowany. Pokazuje, jak korzystać z ioservice w trybie "jednego strzału".

Myślę, że ten przykład to świetne rozwiązanie. Inne rozwiązania podważają przenośność i nie korzystają z usługi ioservice. jeśli przenośność nie jest ważna, a usługa ioservice wydaje się być bardzo obciążona - TO - nie powinieneś używać ASIO. Niezależnie od tego, będziesz miał utworzoną usługę ioservice (prawie wszystkie funkcje ASIO są od niej zależne, nawet gniazda synchronizacji), więc skorzystaj z tego.

Timeout a blocking asio tcp operation

Timeout a blocking asio udp operation

Dokumentacja ASIO została zaktualizowana, więc to sprawdzić dla nowych przykładów, w jaki sposób przezwyciężyć niektóre z ASIO „pułapek” Użyj mieć.

+0

W systemie Windows 'io_service.run_one()' nigdy nie blokuje async_read (przynajmniej w wersji Boost 1.59.0), co powoduje 100% użycie procesora. – rustyx

+3

Przykłady są długie, ponieważ dokumentacja jest 3x zbędna. Nie uważam, że "WELL skomentował". – ChristophK

1

Jeszcze wiele lat po pierwotnym pytaniu nadal nie ma satysfakcjonującej odpowiedzi.

ręcznie za pomocą select nie jest dobrym rozwiązaniem numer deskryptora

  1. plik musi być mniejsza niż 1024
  2. FD może być fałszywie zgłoszony jako gotowy powodu złej sumy kontrolnej.

io_service.run_one() połączeń jest również zły pomysł, ponieważ mogą istnieć inne async opcje, które potrzebuje io_service aby zawsze run(). Dokument doładowania o blokowaniu klienta tcp jest trudny do zrozumienia.

Oto moje rozwiązanie. Kluczową ideą jest następujący:

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

Tutaj Semaphore tylko mutex i condition_variable.
wait_for jest realizowany przez http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

pełny kod jest https://github.com/scinart/cpplib/blob/master/include/asio.hpp
Przykładów jest w https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp
lepszy przykład na https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp

Powiązane problemy