Skip to content

Instantly share code, notes, and snippets.

@tivrfoa
Created April 28, 2025 00:18
Show Gist options
  • Save tivrfoa/c4f6c305b0cf1d708dac0ebec30f68b3 to your computer and use it in GitHub Desktop.
Save tivrfoa/c4f6c305b0cf1d708dac0ebec30f68b3 to your computer and use it in GitHub Desktop.
Rust code for reading and writing to a TCP connection - useful to communicate with netcat (nc)
// using threads so it can read and write without blocking
use std::io::{self, prelude::*, BufReader, BufWriter, Write};
use std::net::TcpStream;
use std::env;
use std::thread;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
eprintln!("Usage: {} <server_ip> <port>", args[0]);
return Ok(());
}
let server_ip = &args[1];
let port = &args[2];
let server_address = format!("{}:{}", server_ip, port);
let stream = match TcpStream::connect(&server_address) {
Ok(stream) => stream,
Err(e) => {
eprintln!("Failed to connect to {}: {}", server_address, e);
return Ok(());
}
};
println!("Successfully connected to {}", server_address);
// Clone the stream for use in multiple threads.
let stream_for_read = stream.try_clone()?;
let stream_for_write = stream;
// --- Read Thread ---
// Spawn a thread to handle reading from the server.
thread::spawn(move || {
let mut stream_reader = BufReader::new(stream_for_read);
let mut response = String::new();
loop {
response.clear();
match stream_reader.read_line(&mut response) {
Ok(0) => {
println!("Server closed the connection.");
break;
}
Ok(_) => {
// println!("Received: {}", response.trim());
print!("{response}");
}
Err(e) => {
eprintln!("Error reading from server: {}", e);
break;
}
}
}
});
// --- Main Thread (User Input) ---
let stdin = io::stdin();
let mut reader = BufReader::new(stdin);
let mut stream_writer = BufWriter::new(stream_for_write);
loop {
print!("> ");
io::stdout().flush()?;
let mut line = String::new();
reader.read_line(&mut line)?;
let message = line.trim().to_string(); // Convert to String to send
if message.to_lowercase() == "quit" {
break;
}
if stream_writer.write_all(message.as_bytes()).is_err() ||
stream_writer.write_all(b"\n").is_err() ||
stream_writer.flush().is_err() {
eprintln!("Error writing to server. Connection may be broken.");
break;
}
}
println!("Main thread finished.");
Ok(())
}
@tivrfoa
Copy link
Author

tivrfoa commented Apr 28, 2025

// Using channel and another thread

use std::io::{self, prelude::*, BufReader, BufWriter, Write};
use std::net::TcpStream;
use std::env;
use std::thread;
use std::sync::mpsc;

fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();

    if args.len() != 3 {
        eprintln!("Usage: {} <server_ip> <port>", args[0]);
        return Ok(());
    }

    let server_ip = &args[1];
    let port = &args[2];
    let server_address = format!("{}:{}", server_ip, port);

    match TcpStream::connect(&server_address) {
        Ok(stream) => {
            println!("Successfully connected to {}", server_address);

            // Clone the stream for use in multiple threads.
            let stream_for_read = stream.try_clone()?;
            let stream_for_write = stream;

            // Create a channel for sending messages from the user input thread to the writer thread.
            let (tx, rx) = mpsc::channel::<String>();

            // --- Read Thread ---
            // Spawn a thread to handle reading from the server.
            thread::spawn(move || {
                let mut stream_reader = BufReader::new(stream_for_read);
                let mut response = String::new();
                loop {
                    match stream_reader.read_line(&mut response) {
                        Ok(0) => {
                            println!("Server closed the connection.");
                            break;
                        }
                        Ok(_) => {
                            // println!("Received: {}", response.trim());
                            print!("{response}");
                        }
                        Err(e) => {
                            eprintln!("Error reading from server: {}", e);
                            break;
                        }
                    }
                }
            });

            // --- Write Thread ---
            // Spawn a thread to handle writing to the server.
            thread::spawn(move || {
                let mut stream_writer = BufWriter::new(stream_for_write);
                while let Ok(message) = rx.recv() {
                    if message.to_lowercase() == "quit" {
                        break;
                    }
                    if stream_writer.write_all(message.as_bytes()).is_err() ||
                       stream_writer.write_all(b"\n").is_err() ||
                       stream_writer.flush().is_err() {
                        eprintln!("Error writing to server.  Connection may be broken.");
                        break;
                    }
                }
                println!("Write thread finished.");
            });

            // --- Main Thread (User Input) ---
            // The main thread now handles user input and sends it to the write thread via the channel.
            let stdin = io::stdin();
            let mut reader = BufReader::new(stdin);

            loop {
                print!("> ");
                io::stdout().flush()?;

                let mut line = String::new();
                reader.read_line(&mut line)?;
                let trimmed_line = line.trim().to_string(); // Convert to String to send

                // Send the message to the writer thread.
                if tx.send(trimmed_line.clone()).is_err() {
                    eprintln!("Failed to send message to writer thread.");
                    break; // Exit the loop if sending fails (e.g., if the write thread panics)
                }

                if trimmed_line.to_lowercase() == "quit" {
                    break;
                }
            }
            println!("Main thread finished.");
            Ok(())
        }
        Err(e) => {
            eprintln!("Failed to connect to {}: {}", server_address, e);
            Ok(())
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment