diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.gp.md b/.gp.md new file mode 100644 index 0000000..9d200f1 --- /dev/null +++ b/.gp.md @@ -0,0 +1,215 @@ +Additional context is provided below. + +I have the following from soulseek_download/src/main.rs: + +```rust +use std::collections::VecDeque; +use std::io::{self, Write}; +use std::net::{TcpStream, ToSocketAddrs}; +use std::sync::{Arc, Barrier, Condvar, Mutex}; +use std::thread::{self, sleep}; +use std::time::Duration; + +mod buffered_reader; +mod message; +mod message_factory; + +#[derive(Debug, Clone)] +struct ServerAddress { + host: String, + port: u16, +} + +struct Client { + address: ServerAddress, + username: String, + password: String, +} + +struct Server { + address: ServerAddress, + stream: Arc>, + message_queue: Arc<(Mutex>>, Condvar)>, +} + +impl Server { + /// Create a new instance of Server, returning a Result + fn new(address: ServerAddress) -> Result { + println!("Connecting to server at {}:{}", address.host, address.port); + + // Convert the host and port into a socket address + let socket_address = format!("{}:{}", address.host, address.port) + .to_socket_addrs()? + .next() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Invalid address"))?; + + // Establish the connection with a timeout + let stream = TcpStream::connect_timeout(&socket_address, Duration::from_secs(10))?; + + // Set the stream to non-blocking mode if needed or configure timeouts + stream.set_read_timeout(Some(Duration::from_secs(5)))?; + stream.set_write_timeout(Some(Duration::from_secs(5)))?; + + let message_queue = Arc::new((Mutex::new(VecDeque::new()), Condvar::new())); // Initialize the message queue with Condvar + + let server = Self { + address, + stream: Arc::new(Mutex::new(stream)), + message_queue, + }; + + server.start_read_write_loops(); + + Ok(server) + } + /// Start reading and writing loops in separate threads + fn start_read_write_loops(&self) { + // Create a channel to send messages to the write thread + let read_stream = Arc::clone(&self.stream); + let write_stream = Arc::clone(&self.stream); + + let barrier = Arc::new(Barrier::new(2)); + let read_barrier = barrier.clone(); + let write_barrier = barrier.clone(); + let message_queue = self.message_queue.clone(); + + // Spawn a thread to handle reading from the server + thread::spawn(move || { + read_barrier.wait(); + let mut buffered_reader = buffered_reader::BufferedReader::new(); + + loop { + match buffered_reader.read_from_socket(&mut read_stream.lock().unwrap()) { + Ok(_) => {} + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue, + Err(ref e) if e.kind() == io::ErrorKind::TimedOut => { + println!("Read operation timed out"); + continue; + } + Err(e) => { + eprintln!("Error reading from server: {}", e); + break; + } + } + + match buffered_reader.extract_message() { + Ok(Some(mut message)) => { + println!("Received message: {:?}", message.get_data()); + self.handle_message(&mut message); + } + Err(e) => { + println!("Error extracting message: {}", e) + } + Ok(None) => continue, + } + } + }); + thread::spawn(move || { + write_barrier.wait(); + + loop { + let (lock, cvar) = &*message_queue; + let mut queue = lock.lock().unwrap(); + + // Wait for a message to be added to the queue + while queue.is_empty() { + queue = cvar.wait(queue).unwrap(); + } + + // Pop and send the message + if let Some(message) = queue.pop_front() { + match write_stream.lock().unwrap().write_all(&message) { + Ok(_) => println!("Sent buffered message: {:?}", message), + Err(e) => { + eprintln!("Failed to send buffered message: {}", e); + // Handle error appropriately + break; + } + } + } + } + }); + } + + fn queue_message(&self, message: Vec) { + let (lock, cvar) = &*self.message_queue; + let mut queue = lock.lock().unwrap(); + queue.push_back(message); + cvar.notify_one(); + } + + fn login(&mut self, username: &str, password: &str) -> Result<(), std::io::Error> { + self.queue_message(message_factory::build_init_message().get_data()); + self.queue_message(message_factory::build_login_message(username, password).get_data()); + Ok(()) + } + + fn handle_message(&mut self, message: &mut message::Message) { + let code = message.get_code(); + match code { + 1 => { + println!("Received login response"); + let response = message.read_int8(); + if response == 1 { + println!("Login successful"); + } else { + println!("Login failed"); + } + } + _ => { + println!("Received unknown message with code {}", code); + } + } + } +} + +impl Client { + fn new(address: ServerAddress, username: String, password: String) -> Self { + Self { + address, + username, + password, + } + } + fn connect(&self) { + // Attempt to create a server instance + match Server::new(self.address.clone()) { + Ok(mut server) => { + println!( + "Connected to server at {}:{}", + server.address.host, server.address.port + ); + // Attempt to login + println!("Logging in as {}", self.username); + + if let Err(e) = server.login(&self.username, &self.password) { + eprintln!("Error during login: {}", e); + } + } + Err(e) => eprintln!("Error connecting to server: {}", e), + } + } +} +// ServerAddress { +// host: String::from("server.slsknet.org"), +// port: 2242, +// }, + +fn main() { + let client = Client::new( + ServerAddress { + // host: String::from("server.slsknet.org"), + host: String::from("localhost"), + port: 2242, + }, + String::from("insane_in_the_brain2"), + String::from("invalid"), + // String::from("13375137"), + ); + client.connect(); + loop { + thread::park(); + } +} +```