2011-02-07 9 views
17

Mam asynchronicznie klienta i serwer przy użyciu boost::asio. Chcę dodać czasy oczekiwania, aby zamknąć połączenie i potencjalnie spróbować ponownie, jeśli coś pójdzie nie tak.Czy wywołania asynchroniczne :: asio są automatycznie wyłączane?

Moja początkowa myśl polegała na tym, że za każdym razem, gdy wywoływam funkcję async_, powinienem również uruchomić deadline_timer, aby wygasł po tym, jak spodziewam się zakończenia operacji asynchronicznej. Teraz zastanawiam się, czy jest to bezwzględnie konieczne w każdym przypadku.

Na przykład

  • async_resolve przypuszczalnie wykorzystuje resolverowi systemu, który ma wbudowane w nią limitu czasu (np RES_TIMEOUT w resolv.h ewentualnie zastąpione przez konfigurację w /etc/resolv.conf). Dodając własny zegar, mogę być w konflikcie z tym, jak użytkownik chce, aby jego resolwer zadziałał.

  • Dla async_connect The connect(2) syscall ma jakiś limit czasu wbudowany w to

  • itp

Więc który (jeśli w ogóle) są gwarantowane async_ rozmowy zadzwonić swoje ładowarki w ramce " rozsądne "ramy czasowe? A jeśli operacja [może | zrobić] czas oczekiwania, to obsługa zostanie przekazana jako błąd basic_errors::timed_out lub coś innego?

+0

Musiałem również trzymać się własnego "deadline_timer", więc byłbym bardzo zainteresowany, aby zobaczyć, czy to nie jest konieczne. – chrisaycock

+0

+1 interesujące pytanie. Myślę, że odpowiedź byłaby w dużej mierze zależna od platformy. Zakładam, że zależy ci na Linuksie? –

Odpowiedz

29

Więc zrobiłem kilka testów. Na podstawie moich wyników jest jasne, że zależą one od implementacji systemu operacyjnego. Dla porównania przetestowałem to z jądrowym kernelem Fedory: 2.6.35.10-74.fc14.x86_64.

Najważniejsze jest to, że async_resolve() wygląda na to jedyny przypadek, w którym może móc uciec bez ustawiania deadline_timer. Jest to praktycznie wymagane w każdym innym przypadku dla rozsądnego zachowania.


async_resolve()

Wywołanie async_resolve() skutkowało 4 zapytań 5 sekund od siebie. Handler został wywołany 20 sekund po żądaniu z błędem boost::asio::error::host_not_found.

Mój resolver domyślnie przyjmuje 5-sekundowy limit czasu z 2 próbami (resolv.h), więc wydaje się, że wysyła dwa razy więcej skonfigurowanych zapytań. Zachowanie można modyfikować, ustawiając options timeout i options attempts w /etc/resolv.conf. W każdym przypadku liczba wysłanych zapytań była dwukrotnie większa, niż ustawiono attempts, a handler został wywołany z błędem host_not_found.

Dla testu pojedynczy skonfigurowany serwer nazw był routowany w czarnej dziurze.


async_connect()

Wywołanie async_connect() z black-hole-kierowane okolicy spowodowało handler miano z błędem boost::asio::error::timed_out po ~ 189 sekund.

Stos wysłał początkowy SYN i 5 ponownych prób. Pierwsza próba została wysłana po 3 sekundach, a czas oczekiwania na powtórzenie podwaja się za każdym razem (3 + 6 + 12 + 24 + 48 + 96 = 189). Liczba powtórzeń mogą być zmieniane:

% sysctl net.ipv4.tcp_syn_retries 
net.ipv4.tcp_syn_retries = 5 

Domyślnym 5 wybrany jest zgodne z RFC 1122 (4.2.3.5):

[programatorów retransmisji] dla segmentu SYN musi być ustawić wystarczająco duży, aby zapewnić retransmisję segmentu przez co najmniej 3 minuty. Aplikacja może zamknąć połączenie (tj. Zrezygnować z próby otwarcia) wcześniej, oczywiście.

3 minuty = 180 sekund, chociaż w RFC nie określono górnej granicy. Nic nie powstrzyma implementacji przed ponowną próbą na zawsze.


async_write()

Dopóki bufor wyślij gniazda nie była pełna, uchwyt ten nazywany był zawsze od razu.

Mój test ustanowił połączenie TCP i minutę później ustaw czasomierz, aby zadzwonić pod numer async_write(). Podczas minutę którym połączenie zostało nawiązane, ale przed wywołaniem async_write(), próbowałem wszelkiego rodzaju chaos:

  • Ustawienie routera do dalszego czarno-dołkowego późniejszego ruchu do miejsca przeznaczenia.
  • Wyczyszczenie sesji w zaporze sieciowej, aby odpowiadała fałszywymi sygnałami RST od miejsca docelowego.
  • Odłączenie mojego Ethernet
  • Running /etc/init.d/network stop

Bez względu na to, co zrobiłem, następny async_write() natychmiast zadzwonić do jego obsługi zgłosić sukces.

W przypadku, gdy zapora fałszywej RST, połączenie zostało natychmiast zamknięte, ale nie miał możliwości dowiedzenia się, że dopóki nie próbował następny operację (co natychmiast zgłosić boost::asio::error::connection_reset). W innych przypadkach połączenie pozostanie otwarte i nie będzie zgłaszało mi błędów, aż w końcu upłynie 17-18 minut później.

Najgorszy przypadek dla async_write() dotyczy tego, czy host wysyła retransmisje, a bufor wysyłania jest pełny.Jeśli bufor jest pełny, async_write() nie wywoła jego obsługi do czasu upłynięcia czasu retransmisji. domyślne Linux do 15 retransmisji:

% sysctl net.ipv4.tcp_retries2 
net.ipv4.tcp_retries2 = 15 

Czas pomiędzy retransmisji większą od siebie (i zależy od wielu czynników, takich jak w przewidywanym czasie obie specyficznego połączenia), lecz jest zaciśnięta na 2 minuty. Tak więc przy domyślnych 15 retransmisjach i najgorszym 2-minutowym limicie czasu górna granica wynosi 30 minut, aby wywołać procedurę obsługi async_write(). Po wywołaniu błąd jest ustawiony na boost::asio::error::timed_out.


async_read()

To nigdy nie powinno się nazywać jego obsługi o ile połączenie zostanie ustanowione i nie jest odbierany danych. Nie miałem czasu go przetestować.

+2

Ten powinien być odpowiedzią ... daje o wiele więcej szczegółów niż moja krótka odpowiedź. – diverscuba23

10

Te dwie rozmowy MOGĄ mieć limity czasu, które zostaną zarekomendowane do obsługi, ale możesz być zaskoczony czasem, który upłynie, zanim któryś z tych limitów się skończy. (Wiem, że pozwoliłem, aby połączenie zostało po prostu usatysfakcjonowane i spróbuj połączyć się z jednym połączeniem przez ponad 10 minut z boost::asio przed zabiciem procesu). Również wywołania async_read i async_write nie mają limitów czasu z nimi związanych, więc jeśli chcesz mieć limity czasu na czytanie i zapisywanie, nadal będziesz potrzebował deadline_timer.

+1

+1 w razie wątpliwości bądź wyraźny. Dodaj własne liczniki, jeśli zachowanie nie jest udokumentowane. –

+0

Tak. To jest prawie to, co znalazłem. Chociaż 'async_read' i' async_write' * mają * mają timeouty w tym sensie, że nadmierna retransmisja ostatecznie spowoduje, że połączenie zakończy się po kilkudziesięciu minutach. – eater

Powiązane problemy