I'm writing a small TCP game server with tokio. It accepts connections, manages them in a vec, and broadcasts game state periodically. Here's what that code looks like so far:
use std::error::Error;
use std::time::Duration;
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let mut tick_interval = tokio::time::interval(Duration::from_secs(1));
let mut tcp_listener = TcpListener::bind("127.0.0.1:5000").await?;
let mut streams = Vec::new();
loop {
tokio::select! {
_ = tick_interval.tick() => println!("tick"),
accept_result = tcp_listener.accept() => {
match accept_result {
Ok((stream, sock_addr)) => streams.push(stream),
Err(err) => {
eprintln!("{}", err);
break
},
}
},
// TODO How to await on a message being received from any one of our tcp streams?
}
}
Ok(())
}
I'd like to add a third branch to the tokio::select!
statement to read messages from each TcpStream
and modify some shared state based on them, but I'm not sure of the best way to do that. My ideas are:
a) Spawn a task for each connection in the accept
branch, as well as two channels to send and receive messages going between the tokio::select!
loop and the task.
b) Merge the streams together and await
on the result in a third branch in the tokio::select!
statement.
b) seems way more elegant to me. tokio::stream::StreamExt::merge could do the trick, or if I map the streams into an iterator of futures::stream::Stream
then I could use futures::stream::select_all or another combinator to do the trick.
Am I overlooking any obvious solutions, or are any of these proposals clearly better than the others?