This article explains how to build a simple networked file transfer tool using Rust, focusing on client-server communication over TCP.
In this article, we explain how to build a simple networked file transfer tool—a basic client-server application using Rust. The client reads a file in chunks and sends it to the server over a TCP connection. The server reassembles these chunks, writes them to disk, and acknowledges the successful file transfer.
The server component consists of a TCP listener that binds to a specified port, accepts client connections, reads file chunks, and writes them to disk. Once the entire file is received, the server sends an acknowledgment back to the client.
The client takes user input for the file path, connects to the server via TCP, reads the file in manageable chunks, and sends these chunks to the server. It includes error handling and waits for the server’s acknowledgment before closing the connection.
The file transfer is optimized for large files by reading and sending data in chunks. If a file with the same name already exists on the server, it will be overwritten.
Create a new Rust project by executing the following command in your project directory:
Copy
Ask AI
cargo new tcp_file_transfer
Inside the src directory, create a new folder named bin and add two files: client.rs and server.rs. This separation keeps the server and client logic distinct.
The following code demonstrates the server functionality, including error handling and proper chunked file writing:
Copy
Ask AI
use std::fs::File;use std::io::{Read, Write};use std::net::{TcpListener, TcpStream};use std::thread;fn handle_client(mut stream: TcpStream) -> std::io::Result<()> { // Create a new file to store the incoming data let mut file = File::create("received_file.txt")?; let mut buffer = [0u8; 1024]; // 1 KB buffer loop { // Read data from the stream into the buffer match stream.read(&mut buffer) { Ok(0) => { // End-of-file reached; client closed the connection println!("End of file reached, client closed the connection."); break; } Ok(bytes_read) => { println!("Received {} bytes", bytes_read); file.write_all(&buffer[..bytes_read])?; } Err(e) => { eprintln!("Error reading from stream: {}", e); break; } } } // Send an acknowledgment back to the client println!("File received successfully."); match stream.write_all(b"Transfer complete") { Ok(_) => println!("Sent acknowledgment to the client."), Err(e) => eprintln!("Error sending acknowledgment: {}", e), } Ok(())}fn start_server() -> std::io::Result<()> { // Bind the server to localhost on port 8080 let listener = TcpListener::bind("127.0.0.1:8080")?; println!("Server listening on port 8080..."); // Accept and handle incoming client connections for stream in listener.incoming() { match stream { Ok(stream) => { thread::spawn(move || { if let Err(e) = handle_client(stream) { eprintln!("Error handling client: {}", e); } }); } Err(e) => { eprintln!("Error accepting connection: {}", e); } } } Ok(())}fn main() -> std::io::Result<()> { start_server()}
The client code prompts the user for the file path, reads the file in chunks, sends each chunk to the server, and waits for an acknowledgment upon completion.
Copy
Ask AI
use std::fs::File;use std::io::{self, Read, Write};use std::net::{TcpStream, Shutdown};use std::process;fn get_file_path_from_user() -> String { println!("Please enter the absolute path of the file to send:"); let mut file_path = String::new(); io::stdin() .read_line(&mut file_path) .expect("Failed to read line"); file_path.trim().to_string()}fn send_file(file_path: &str) -> io::Result<()> { // Open the specified file let mut file = File::open(file_path)?; // Connect to the server at localhost:8080 let mut stream = TcpStream::connect("127.0.0.1:8080")?; println!("Connected to server at 127.0.0.1:8080"); let mut buffer = [0u8; 1024]; // 1 KB buffer for file data loop { let bytes_read = match file.read(&mut buffer) { Ok(0) => break, // End-of-file reached Ok(n) => n, Err(e) => { eprintln!("Error reading file: {}", e); return Err(e); } }; stream.write_all(&buffer[..bytes_read])?; } // Signal to the server that no more data will be sent stream.shutdown(Shutdown::Write)?; // Read and display the server's acknowledgment let mut response = [0u8; 1024]; match stream.read(&mut response) { Ok(bytes_read) => { println!("{}", String::from_utf8_lossy(&response[..bytes_read])); } Err(e) => { eprintln!("Error reading acknowledgment: {}", e); } } Ok(())}fn main() { let file_path = get_file_path_from_user(); if let Err(e) = send_file(&file_path) { eprintln!("File transfer failed: {}", e); process::exit(1); }}
Build the ProjectRun the following command to build the project:
Copy
Ask AI
cargo build
Run the ServerOpen a terminal and run the server with:
Copy
Ask AI
cargo run --quiet --bin server
You should see output like:
Copy
Ask AI
Server listening on port 8080...
Run the ClientOpen another terminal and run the client with:
Copy
Ask AI
cargo run --quiet --bin client
When prompted, enter the absolute file path of the file to send. An example interaction is shown below:
Copy
Ask AI
Please enter the absolute path of the file to send:/Users/yourname/projects/tcp_file_transfer/temp.txtConnected to server at 127.0.0.1:8080Transfer complete
On the server terminal, you will see logs detailing each received chunk, the end-of-file notification, and the acknowledgment status.
Copy
Ask AI
cargo run --quiet --bin serverServer listening on port 8080...Received 1024 bytesReceived 1024 bytesReceived 1024 bytesReceived 960 bytesEnd of file reached, client closed the connection.File received successfully.Sent acknowledgment to the client.
In this article, we built a networked file transfer application using Rust and TCP sockets. The client reads a file in chunks and sends it to the server, which writes the chunks to disk and sends back an acknowledgment upon successful completion.
This basic application can be extended with features such as file compression, encryption, or support for transferring multiple files concurrently.
We hope you enjoyed building this project and learning about networked file transfers with Rust!