2011-02-07 12 views
17

Ho un client e un server che utilizza boost::asio in modo asincrono. Voglio aggiungere alcuni timeout per chiudere la connessione e potenzialmente riprovare se qualcosa va storto.Do boost: le chiamate asio async scadono automaticamente?

Il mio pensiero iniziale era che ogni volta che io chiamo una funzione async_ dovrei anche iniziare una deadline_timer per scadere dopo mi aspetto che l'operazione asincrona per il completamento. Ora mi chiedo se sia strettamente necessario in ogni caso.

Ad esempio:

  • async_resolve utilizza presumibilmente resolver del sistema che ha timeout incorporati in esso (per esempio RES_TIMEOUT in resolv.h eventualmente sovrascritto configurazione in /etc/resolv.conf). Aggiungendo il mio timer personale, potrei entrare in conflitto con il modo in cui l'utente vuole che il suo risolutore funzioni.

  • Per async_connect, la chiamata di sistema connect(2) ha una sorta di timeout incorporato in esso

  • ecc

Quindi, quale (se presente) async_ chiamate sono garantiti per chiamare i loro gestori all'interno di una " ragionevole "periodo di tempo? E se un'operazione [può | fa] timeout, il gestore deve passare l'errore basic_errors::timed_out o qualcos'altro?

+0

Ho dovuto limitarmi a creare il mio 'deadline_timer', quindi sarei molto interessato a vedere se questo non è necessario. – chrisaycock

+0

+1 domanda interessante. Penso che la risposta dipenda in gran parte dalla piattaforma. Suppongo che ti interessi di Linux? –

risposta

29

Così ho fatto alcuni test. Sulla base dei miei risultati, è chiaro che dipendono dall'implementazione del sistema operativo sottostante. Come riferimento, ho provato questo con un kernel Fedora originale: 2.6.35.10-74.fc14.x86_64.

La linea di fondo è che async_resolve() sembra essere l'unico caso in cui si potrebbe essere in grado di uscire senza impostare una deadline_timer. È praticamente richiesto in ogni altro caso per un comportamento ragionevole.


async_resolve()

Una chiamata a async_resolve() portato in 4 interrogazioni 5 secondi di distanza. Il gestore è stato chiamato 20 secondi dopo la richiesta con l'errore boost::asio::error::host_not_found.

Il mio resolver ha un timeout predefinito di 5 secondi con 2 tentativi (resolv.h), quindi sembra che invii il doppio del numero di query configurate. Il comportamento è modificabile impostando options timeout e options attempts in /etc/resolv.conf. In ogni caso il numero di query inviate era doppio rispetto a attempts e il gestore veniva chiamato con l'errore host_not_found in seguito.

Per il test, il server dei nomi configurato singolo è stato instradato in un buco nero.


async_connect()

Chiamata async_connect() con una destinazione black-hole-instradato portato nel gestore chiamato con l'errore boost::asio::error::timed_out dopo ~ 189 secondi.

Lo stack ha inviato i tentativi iniziali SYN e 5. Il primo tentativo è stato inviato dopo 3 secondi, con il timeout del tentativo di raddoppio ogni volta (3 + 6 + 12 + 24 + 48 + 96 = 189). Il numero di tentativi può essere modificato:

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

Il valore predefinito di 5 è scelto di osservare le RFC 1122 (4.2.3.5):

[I timer di ritrasmissione] per un segmento SYN DEVE essere impostare abbastanza grande per fornire la ritrasmissione del segmento per almeno 3 minuti. L'applicazione può chiudere la connessione (vale a dire, rinunciare al tentativo aperto) prima, naturalmente.

3 minuti = 180 secondi, sebbene l'RFC non sembri specificare un limite superiore. Non c'è nulla che impedisca a un'implementazione di riprovare per sempre.


async_write()

Finché buffer di trasmissione del socket non era pieno, questo gestore è stato sempre chiamato subito.

Il mio test ha stabilito una connessione TCP e impostato un timer per chiamare async_write() un minuto dopo. Durante il minuto in cui è stata stabilita la connessione, ma prima della chiamata async_write(), ho provato tutti i tipi di caos:

  • Impostazione di un router a valle al buco nero traffico successivo alla destinazione.
  • Cancellare la sessione in un firewall downstream in modo che risponda con gli RST falsificati dalla destinazione.
  • scollega il mio Ethernet
  • Esecuzione /etc/init.d/network stop

Non importa quello che ho fatto, la prossima async_write() sarebbe chiamare immediatamente il suo gestore di segnalare il successo.

Nel caso in cui il firewall spoofing della RST, la connessione è stata chiusa immediatamente, ma non ho avuto modo di sapere che fino a quando ho tentato il successiva operazione (che avrebbe riferire immediatamente boost::asio::error::connection_reset). Negli altri casi, la connessione rimarrebbe aperta e non mi riporterebbe errori fino a quando non si esaurisce dopo 17-18 minuti.

Il caso peggiore per async_write() è se l'host viene ritrasmesso e il buffer di invio è pieno.Se il buffer è pieno, async_write() non chiamerà il gestore fino al timeout della ritrasmissione. di default di Linux a 15 ritrasmissioni:

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

Il tempo tra le ritrasmissioni aumenta dopo ogni (e si basa su diversi fattori quali il tempo stimato di andata e ritorno del collegamento specifico), ma viene bloccato a 2 minuti. Pertanto, con le 15 ritrasmissioni predefinite e il timeout di 2 minuti nel caso peggiore, il limite superiore è di 30 minuti per il gestore async_write() da chiamare. Quando viene chiamato, l'errore è impostato su boost::asio::error::timed_out.


async_read()

questo non dovrebbe mai chiamare il suo gestore fino a quando la connessione è stabilita e nessun dato è ricevuto. Non ho avuto il tempo di testarlo.

+2

Questa dovrebbe essere la risposta ... fornisce dettagli molto più rilevanti di quella che era la mia risposta breve. – diverscuba23

10

Queste due chiamate MAGGIO hanno scadenze che vengono offerte ai vostri gestori, ma potreste essere soppressi per il tempo necessario prima di una di queste volte. (So ​​che ho lasciato che una connessione si sedesse e provasse a connettermi su una singola chiamata di connessione per oltre 10 minuti con boost::asio prima di uccidere il processo). Anche le chiamate async_read e async_write non hanno timeout associati, quindi se si desidera avere timeout sulle letture e le scritture, sarà comunque necessario un deadline_timer.

+1

+1 in caso di dubbio, essere esplicito. Aggiungi i tuoi timer personali se il comportamento non è documentato. –

+0

Sì. Questo è praticamente quello che ho trovato. Anche se 'async_read' e' async_write' * do * hanno timeout nel senso che ritrasmissioni eccessive finiranno per causare la chiusura della connessione dopo decine di minuti. – eater

Problemi correlati