In Boost Asio it's normal to register a callback for when data arrives in a socket using async_read which receives a buffer and a callback. When the buffer is full the callback is executed.
How do I do the same thing using Tokio for a long-lived connection?
.await doesn't block threads. It only suspends execution of code until the data arrives. It behaves exactly like turning rest of that async block into a callback (behind the scenes it is a callback from a Waker given by read_exact to runtime that executes that async block).
I know I've accepted a solution but just so I don't need to open a new thread, is there a way to always be notified whenever data arrives in the socket, assuming the connection will never be closed?
Async code can be stored as an object — a Future, but it doesn't do anything while it's sitting in a struct. It needs to be spawned to run.
You can tie it to an object using channels:
let (sender, receiver) = tokio::sync::mpsc::channel(1);
// add your own error handling
spawn(async move {
loop {
let bytes_read = socket.read(&mut buf).await?;
// it'll fail when receiver is dropped
sender.send(buf[0..bytes_read].to_vec())?;
}
});
// store receiver in a struct, and later you can do:
somestruct.receiver.recv().await;
but that's a bit overcomplicated, because usually you'd store socket in a struct, and just read from it in async fn.
Callback-based design is rather different than async/future-based design, so I suspect you'll need to adjust your design to fit async code, instead of trying to make async functions work like callbacks.