use tokio::net::TcpListener;
+async fn handle_tcp_listener(listener: TcpListener) -> Result<(), anyhow::Error> {
+ loop {
+ let (mut stream, _) = listener.accept().await?;
+ tokio::spawn(async move {
+ let (mut reader, mut writer) = stream.split();
+ tokio::io::copy(&mut reader, &mut writer).await.unwrap();
+ });
+ }
+}
+
// TODO: write an echo server that accepts TCP connections on two listeners, concurrently.
// Multiple connections (on the same listeners) should be processed concurrently.
// The received data should be echoed back to the client.
pub async fn echoes(first: TcpListener, second: TcpListener) -> Result<(), anyhow::Error> {
- todo!()
+ let handle1 = tokio::spawn(handle_tcp_listener(first));
+ let handle2 = tokio::spawn(handle_tcp_listener(second));
+
+ let (outcome1, outcome2) = tokio::join!(handle1, handle2);
+ outcome1??;
+ outcome2??;
+ Ok(())
}
#[cfg(test)]
// TODO: Implement the `fixed_reply` function. It should accept two `TcpListener` instances,
// accept connections on both of them concurrently, and always reply to clients by sending
// the `Display` representation of the `reply` argument as a response.
-use std::fmt::Display;
+use std::{fmt::Display, sync::Arc};
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
// `T` cannot be cloned. How do you share it between the two server tasks?
T: Display + Send + Sync + 'static,
{
- todo!()
+ let reply = Arc::new(reply);
+ let handle1 = tokio::spawn(handle_listener(first, reply.clone()));
+ let handle2 = tokio::spawn(handle_listener(second, reply));
+ let _ = tokio::join!(handle1, handle2);
+}
+
+async fn handle_listener<T>(listener: TcpListener, reply: Arc<T>)
+where
+ T: Display + Send + Sync + 'static,
+{
+ loop {
+ let (mut stream, _) = listener.accept().await.unwrap();
+ stream
+ .write_all(reply.to_string().as_bytes())
+ .await
+ .unwrap();
+ }
}
#[cfg(test)]
}
async fn example() {
- let non_send = Rc::new(1);
+ {
+ let non_send = Rc::new(1);
+ println!("{}", non_send);
+ }
yield_now().await;
- println!("{}", non_send);
}