Come eseguo una lettura senza blocco usando asio?
-
19-08-2019 - |
Domanda
Sto tentando di utilizzare boost :: asio per leggere e scrivere da un dispositivo su una porta seriale. Entrambi i blocchi boost :: asio: read () e boost :: asio :: serial_port :: read_some () quando non c'è nulla da leggere. Vorrei invece rilevare questa condizione e scrivere un comando sulla porta per avviare il dispositivo.
Come posso rilevare che non sono disponibili dati?
Se necessario, posso fare tutto in modo asincrono, preferirei evitare l'eventuale complessità aggiuntiva.
Soluzione
In realtà hai un paio di opzioni. Puoi utilizzare la funzione async_read_some
integrata nella porta seriale, oppure puoi utilizzare la funzione autonoma boost :: asio :: async_read
(o async_read_some
).
Incontrerai comunque la situazione in cui sei effettivamente "bloccato", poiché nessuno di questi chiamerà il callback a meno che (1) i dati non siano stati letti o (2) si verifichi un errore. Per ovviare a questo, ti consigliamo di utilizzare un oggetto deadline_timer
per impostare un timeout. Se il timeout viene attivato per primo, non sono disponibili dati. Altrimenti, avrai letto i dati.
La complessità aggiunta non è poi così male. Ti ritroverai con due callback con un comportamento simile. Se il "leggi" " o il "timeout" la callback si attiva con un errore, sai che è il perdente della gara. Se uno dei due spara senza errori, allora sai che è il vincitore della gara (e dovresti annullare l'altra chiamata). Nel punto in cui avresti avuto la tua chiamata di blocco a read_some
, ora avrai una chiamata a io_svc.run ()
. La tua funzione continuerà a bloccarsi come prima quando chiama esegui
, ma questa volta controlli la durata.
Ecco un esempio:
void foo()
{
io_service io_svc;
serial_port ser_port(io_svc, "your string here");
deadline_timer timeout(io_svc);
unsigned char my_buffer[1];
bool data_available = false;
ser_port.async_read_some(boost::asio::buffer(my_buffer),
boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
boost::asio::placeholders::error));
io_svc.run(); // will block until async callbacks are finished
if (!data_available)
{
kick_start_the_device();
}
}
void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error || !bytes_transferred)
{
// No data was read!
data_available = false;
return;
}
timeout.cancel(); // will cause wait_callback to fire with an error
data_available = true;
}
void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
if (error)
{
// Data was read and this timeout was canceled
return;
}
ser_port.cancel(); // will cause read_callback to fire with an error
}
Ciò dovrebbe iniziare con solo alcune modifiche qua e là per soddisfare le tue esigenze specifiche. Spero che questo aiuti!
Un'altra nota: non sono stati necessari thread aggiuntivi per gestire i callback. Tutto è gestito all'interno della chiamata a run ()
. Non sono sicuro che tu fossi già a conoscenza di questo ...
Altri suggerimenti
In realtà è molto più semplice delle implicazioni qui fornite, e puoi farlo in modo sincrono:
Supponiamo che la lettura del tuo blocco sia stata qualcosa del genere:
size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
Quindi lo sostituisci con
socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
//do other things here like go and make coffee
len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);
Usi la forma sovraccaricata alternativa di ricezione_di cui hanno quasi tutti i metodi di invio / ricezione. Sfortunatamente accettano un argomento flag ma 0 sembra funzionare bene.
Devi usare la funzione libera asio :: async_read.