2012-05-16 20 views
6

Sto scrivendo un server con la libreria asio di boost. Il server gestisce molte connessioni simultanee utilizzando una raccolta di oggetti Connection (una classe wrapper attorno a boost :: asio :: tcp :: socket). All'interno della classe Connection, il socket viene costantemente letto dall'uso di socket.async_read_some (...) e ogni volta che il gestore di lettura viene richiamato con nuovi dati, socket.async_read_some() viene immediatamente richiamato per ulteriori dati da leggere.boost :: asio :: tcp :: socket Chiudi e annulla senza gestori chiamati

Ora, il server può decidere di disconnettere un client per qualche motivo, quindi la cosa naturale da fare è chiamare connection.close() che a sua volta chiama socket.close(), che farà sì che tutte le operazioni asincrone pendenti essere cancellato. Ciò causa l'invocazione del gestore di lettura (associato a un metodo all'interno della connessione di classe) con boost :: asio :: error :: operation_aborted. E il mio problema è: non voglio che ciò accada.

Dopo socket.close(), mi piacerebbe distruggere il socket e la connessione e quindi rimuovere il suo puntatore dall'elenco dei client attivi del server. Tuttavia il gestore di lettura non verrà chiamato fino alla prossima iterazione di io_service.run(), il che significa che non posso distruggere immediatamente il socket o il gestore di lettura che ho passato a socket.async_read_some() fino a quando il gestore non è stato richiamato con il errore. Quindi devo ritardare la distruzione di quegli oggetti in qualche modo; questo è fastidioso.

c'è un modo sicuro per entrambi

  • Annulla in attesa di operazioni asincrone senza alcun gestore di da richiamare, in modo da poter distruggere in modo sicuro la presa immediatamente dopo socket.close(), o
  • a sapere con certezza quando non è più possibile chiamare altri gestori

O mi sto avvicinando a questo completamente nel modo sbagliato?

risposta

4

Quando un async.operation è completo, riuscito o con errore, viene richiamato il gestore di completamento. Questa è una garanzia importante, e non penso che sia una buona idea provare e "hackerare" questo comportamento. Il problema riscontrato nel tuo caso d'uso è generalmente sovled utilizzando shared_ptr (shared_from_this idiom): bind shared_ptr<Connection> per i gestori, non emettere un altro async_read quando si ottiene operation_aborted (o qualche altro errore), in modo che quando tutti i gestori l'oggetto Connection viene distrutto con il suo socket.

+0

Questo suona molto bene. Io sicuramente non uso abbastanza shared_ptr e non sapevo nemmeno di enable_shared_from_questo. Ho usato per fare questo: "socket.async_connect (endpoint, boost :: bind (& Connection :: done_connect, this, _1))", così ora vorrei aggiungere un ulteriore "shared_from_this()" alla chiamata di bind e aggiornare il firma del metodo per un addizionale shared_ptr anche se l'effettivo shared_ptr non verrebbe nemmeno usato nel gestore? Cioè shared_ptr è lì solo per garantire che l'oggetto esista ancora. Sembra giusto? – jlh

+0

Almeno l'ho implementato in questo modo e ora funziona molto bene. Grazie mille! – jlh

+0

@jlh, non è del tutto corretto dire che il parametro shared_ptr "non verrebbe utilizzato nel gestore". Se si associa una funzione membro a shared_ptr, questa viene memorizzata all'interno del raccoglitore (il functor creato con bind()) e de-referenziato sul richiamo del functor. Di conseguenza, il pointee vive finché dura il functor. –

Problemi correlati