I/O and networking
Read and buffer files with Tokio, then build a TCP echo server and client. Rust examples use AsyncReadExt, AsyncWriteExt, and TcpListener.
Files, byte streams, and buffering#
Async file and socket APIs follow AsyncRead/AsyncWrite. Import extension traits for convenient methods.
Tokio file operations often use blocking threads under the hood. They let your async task wait without occupying a runtime worker, but they do not turn disk hardware into truly nonblocking I/O. read_to_string loads the entire file; for large streams, use bounded buffers or line iteration.
use tokio::{fs, io};
#[tokio::main]
async fn main() -> io::Result<()> {
fs::write("message.txt", "hello\n").await?;
let text = fs::read_to_string("message.txt").await?;
println!("{}", text.trim_end());
fs::remove_file("message.txt").await?;
Ok(())
}use tokio::{fs::File, io};
#[tokio::main]
async fn main() -> io::Result<()> {
let mut source = File::open("input.bin").await?;
let mut target = File::create("output.bin").await?;
let bytes = io::copy(&mut source, &mut target).await?;
println!("copied {bytes} bytes");
Ok(())
}use tokio::io::{self, AsyncBufReadExt, BufReader};
#[tokio::main]
async fn main() -> io::Result<()> {
let mut lines = BufReader::new(io::stdin()).lines();
while let Some(line) = lines.next_line().await? {
println!("line: {line}");
}
Ok(())
}read() may return fewer bytes than requested; Ok(0) means EOF. write() may write only part of a buffer; use write_all() when the whole buffer must be sent.TCP servers and clients#
Bind a listener, accept connections, and give each connection its own task.
The echo server copies bytes until the client closes its write side. tokio::io::copy handles partial reads and writes while the listener accepts other connections.
use tokio::{io, net::TcpListener};
#[tokio::main]
async fn main() -> io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:7000").await?;
loop {
let (stream, peer) = listener.accept().await?;
tokio::spawn(async move {
let (mut reader, mut writer) = stream.into_split();
if let Err(error) = io::copy(&mut reader, &mut writer).await {
eprintln!("{peer}: {error}");
}
});
}
}use tokio::{io::{self, AsyncReadExt, AsyncWriteExt}, net::TcpStream};
#[tokio::main]
async fn main() -> io::Result<()> {
let mut stream = TcpStream::connect("127.0.0.1:7000").await?;
stream.write_all(b"hello\n").await?;
let mut response = [0u8; 6];
stream.read_exact(&mut response).await?;
println!("{}", String::from_utf8_lossy(&response));
Ok(())
}The server accepts connections without a cap. Add a semaphore, timeouts, cancellation, and task tracking before using this pattern in production.
Help improve CodeRef
Allow Google Analytics to measure page visits and use of the guides? It uses cookies after you accept. Your choice won’t affect access to the site. Privacy notice