From bdf70ea8d5503326ac0f3318624616dd9ccc0392 Mon Sep 17 00:00:00 2001 From: David Westgate Date: Sat, 25 Nov 2023 21:09:05 -0800 Subject: [PATCH] client and server refactor --- src/client.rs | 123 ++++++++++++++++++++++---------------------------- src/lib.rs | 5 +- src/server.rs | 116 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 142 insertions(+), 102 deletions(-) diff --git a/src/client.rs b/src/client.rs index 31fcb7e..f9c942f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,6 +4,40 @@ use std::io::{Read, Write}; use std::net::TcpStream; use std::thread; +fn no_param_op(opcode: u8, stream: &mut TcpStream) { + stream.write(&[opcode]).unwrap(); +} + +fn one_param_op(opcode: u8, stream: &mut TcpStream, param: &str) { + let size = param.to_string().capacity() + 1; + let mut out_buf: Vec = vec![0; size]; + out_buf[0] = opcode; + + for i in 1..param.len() + 1 { + out_buf[i] = *param.as_bytes().get(i - 1).unwrap(); + } + stream.write(&out_buf).unwrap(); +} + +fn two_param_op(opcode: u8, stream: &mut TcpStream, param0: &str, param1: &str) { + let size = param0.to_string().capacity() + param1.to_string().capacity() + 2; + let mut out_buf: Vec = vec![0; size]; + let mut byte: usize = 0; + out_buf[byte] = opcode; + byte += 1; + for i in 0..param0.len() { + out_buf[byte] = *param0.as_bytes().get(i).unwrap(); + byte += 1; + } + out_buf[byte] = 0x20; + byte += 1; + for i in 0..param1.len() { + out_buf[byte] = *param1.as_bytes().get(i).unwrap(); + byte += 1; + } + stream.write(&out_buf).unwrap(); +} + fn read_messages(mut stream: TcpStream) { let mut buffer: [u8; 1024] = [0; 1024]; loop { @@ -64,67 +98,8 @@ fn process_message(msg_bytes: &[u8]) { fn disconnect() {} -fn rooms(stream: &mut TcpStream) { - stream.write(&[codes::client::LIST_ROOMS]); -} -fn users(stream: &mut TcpStream) { - stream.write(&[codes::client::LIST_USERS]); -} - -fn msg(stream: &mut TcpStream) {} - -fn join(nick: &str, room: &str, stream: &mut TcpStream) { - let size = room.to_string().capacity() + nick.to_string().capacity() + 2; - let mut out_buf: Vec = vec![0; size]; - let mut byte: usize = 0; - out_buf[byte] = codes::client::JOIN_ROOM; - byte += 1; - for i in 0..nick.len() { - out_buf[byte] = *nick.as_bytes().get(i).unwrap(); - byte += 1; - } - out_buf[byte] = 0x20; - byte += 1; - for i in 0..room.len() { - out_buf[byte] = *room.as_bytes().get(i).unwrap(); - byte += 1; - } - stream.write(&out_buf); -} - fn show(stream: &mut TcpStream) {} -fn leave(nick: &str, room: &str, stream: &mut TcpStream) { - let size = room.to_string().capacity() + nick.to_string().capacity() + 2; - let mut out_buf: Vec = vec![0; size]; - let mut byte: usize = 0; - out_buf[byte] = codes::client::LEAVE_ROOM; - byte += 1; - for i in 0..nick.len() { - out_buf[byte] = *nick.as_bytes().get(i).unwrap(); - byte += 1; - } - out_buf[byte] = 0x20; - byte += 1; - for i in 0..room.len() { - out_buf[byte] = *room.as_bytes().get(i).unwrap(); - byte += 1; - } - stream.write(&out_buf); -} - -fn list( room: &str, stream: &mut TcpStream) { - let size = room.to_string().capacity() +1; - let mut out_buf: Vec = vec![0; size]; - out_buf[0] = codes::client::LIST_USERS_IN_ROOM; - - - for i in 1..room.len()+1 { - out_buf[i] = *room.as_bytes().get(i-1).unwrap(); - } - stream.write(&out_buf); -} - pub fn start() { println!("Starting the IRC client. No spaces allowed in nicknames or room names"); let mut nick: String; @@ -155,32 +130,42 @@ pub fn start() { for i in 1..nick.len() + 1 { buf[i] = *nick.as_bytes().get(i - 1).unwrap(); } - stream.write(&buf); + stream.write(&buf).unwrap(); loop { let inp: String = input!(":"); let cmds: Vec<_> = inp.split(" ").collect(); match *cmds.get(0).unwrap() { "/quit" => disconnect(), - "/rooms" => rooms(&mut stream), - "/users" => users(&mut stream), + "/rooms" => no_param_op(codes::client::LIST_ROOMS, &mut stream), + "/users" => no_param_op(codes::client::LIST_USERS, &mut stream), "/list" => { let room = *cmds.get(1).unwrap(); - list(room, &mut stream); - + one_param_op(codes::client::LIST_USERS_IN_ROOM, &mut stream, room); } "/join" => { let room = *cmds.get(1).unwrap(); - join(&nick, room, &mut stream) + one_param_op(codes::client::JOIN_ROOM, &mut stream, room); } "/show" => show(&mut stream), "/leave" => { - let room = *cmds.get(1).unwrap(); - leave(&nick, room, &mut stream) + let room: &str = *cmds.get(1).unwrap(); + one_param_op(codes::client::LEAVE_ROOM, &mut stream, room); + } + "/msg" => { + let room: &str = *cmds.get(1).unwrap(); + // let message = *cmds. + // two_param_op( + // codes::client::SEND_MESSAGE_TO_ROOM, + // &mut stream, + // param0, + // param1, + // ) } - "/msg" => msg(&mut stream), - _ => msg(&mut stream), + _ => { + println!("Not implemented"); + } } } } else { diff --git a/src/lib.rs b/src/lib.rs index 17df567..a3fc597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,16 +9,19 @@ pub mod codes { pub const REGISTER_NICK: u8 = 0x06; pub const LIST_USERS: u8 = 0x07; pub const LIST_USERS_IN_ROOM: u8 = 0x08; + pub const SEND_MESSAGE_TO_ROOM: u8 = 0x09; } pub const QUIT: u8 = 0x0B; pub const KEEP_ALIVE: u8 = 0x0C; pub const RESPONSE: u8 = 0x0D; pub const RESPONSE_OK: u8 = 0x0E; pub const ERROR: u8 = 0x0F; - + pub mod error { pub const INVALID_ROOM: u8 = 0x10; pub const NICKNAME_COLLISION: u8 = 0x11; pub const SERVER_FULL: u8 = 0x12; + pub const ALREADY_REGISTERED: u8 = 0x13; + pub const NOT_YET_REGISTERED: u8 = 0x14; } } diff --git a/src/server.rs b/src/server.rs index c6775d1..f33617e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,38 +27,53 @@ impl Server { } } +fn send_all(op:u8, listener: TcpListener) { + for tcpstream in listener.incoming() { + match tcpstream { + Ok(mut stream) => { + stream.write_all(&[op]).unwrap(); + }, + Err(_) => { + + } + } + } +} + fn handle_client( server: &Arc>, stream: &mut TcpStream, + nickname: &str, cmd_bytes: &[u8], param_bytes: &[u8], ) { // handle user commands match cmd_bytes[0] { codes::client::REGISTER_NICK => { - let nickname: String = String::from_utf8_lossy(param_bytes).to_string(); - register_nick(server, nickname, stream); + stream + .write_all(&[codes::ERROR, codes::error::ALREADY_REGISTERED]) + .unwrap(); } codes::client::LIST_ROOMS => { let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); let mut buf_out: Vec = Vec::new(); buf_out.extend_from_slice(&[codes::RESPONSE]); - for (room, user) in &unlocked_server.rooms { + for (room, _user) in &unlocked_server.rooms { buf_out.extend_from_slice(room.as_bytes()); buf_out.extend_from_slice(&[0x20]); } - stream.write(&buf_out); + stream.write(&buf_out).unwrap(); } codes::client::LIST_USERS => { let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); let mut buf_out: Vec = Vec::new(); buf_out.extend_from_slice(&[codes::RESPONSE]); - for (user) in &unlocked_server.users { + for user in &unlocked_server.users { buf_out.extend_from_slice(user.as_bytes()); buf_out.extend_from_slice(&[0x20]); } - stream.write(&buf_out); + stream.write(&buf_out).unwrap(); } codes::client::LIST_USERS_IN_ROOM => { @@ -67,15 +82,17 @@ fn handle_client( let mut buf_out: Vec = Vec::new(); buf_out.extend_from_slice(&[codes::RESPONSE]); match unlocked_server.rooms.get(&room) { - Some(l) =>{ + Some(l) => { for ele in l { buf_out.extend_from_slice(ele.as_bytes()); buf_out.extend_from_slice(&[0x20]); } - stream.write_all(&buf_out); - }, - None =>{ - stream.write_all(&[codes::ERROR, codes::error::INVALID_ROOM]); + stream.write_all(&buf_out).unwrap(); + } + None => { + stream + .write_all(&[codes::ERROR, codes::error::INVALID_ROOM]) + .unwrap(); } } } @@ -83,17 +100,15 @@ fn handle_client( codes::client::JOIN_ROOM => { let p: String = String::from_utf8_lossy(param_bytes).to_string(); let params: Vec<&str> = p.split(' ').collect(); - let user = params.get(0).unwrap(); - let room = params.get(1).unwrap(); - join_room(server, *user, room, stream) + let room = params.get(0).unwrap(); + join_room(server, &nickname, room, stream) } codes::client::LEAVE_ROOM => { let p: String = String::from_utf8_lossy(param_bytes).to_string(); let params: Vec<&str> = p.split(' ').collect(); - let user = params.get(0).unwrap(); - let room = params.get(1).unwrap(); - leave_room(server, user, room, stream) + let room = params.get(0).unwrap(); + leave_room(server, &nickname, room, stream) } codes::client::SEND_MESSAGE => { @@ -109,19 +124,21 @@ fn handle_client( // } } -fn register_nick(server: &Arc>, nickname: String, stream: &mut TcpStream) { +fn register_nick(server: &Arc>, nickname: &str, stream: &mut TcpStream) { // Check for nickname collision let mut unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); - if unlocked_server.users.contains(&nickname) { + if unlocked_server.users.contains(nickname) { #[cfg(debug_assertions)] println!("Nickname Collision, {}", nickname); - stream.write_all(&[codes::ERROR, codes::error::NICKNAME_COLLISION]); + stream + .write_all(&[codes::ERROR, codes::error::NICKNAME_COLLISION]) + .unwrap(); } else { // Add the user to the user list - unlocked_server.users.insert(nickname.clone()); + unlocked_server.users.insert(nickname.to_string()); // Send response ok - stream.write_all(&[codes::RESPONSE_OK]); + stream.write_all(&[codes::RESPONSE_OK]).unwrap(); } } @@ -137,7 +154,7 @@ fn join_room(server: &Arc>, user: &str, room: &str, stream: &mut T unlocked_server.rooms.insert(room.to_string(), list); } } - stream.write_all(&[codes::RESPONSE_OK]); + stream.write_all(&[codes::RESPONSE_OK]).unwrap(); } fn leave_room(server: &Arc>, user: &str, room: &str, stream: &mut TcpStream) { @@ -148,21 +165,26 @@ fn leave_room(server: &Arc>, user: &str, room: &str, stream: &mut l.retain(|item| item != user); if l.len() == 0 { unlocked_server.rooms.remove(room); - stream.write_all(&[codes::RESPONSE_OK]); + stream.write_all(&[codes::RESPONSE_OK]).unwrap(); } else if l.len() == before_len { - stream.write_all(&[codes::ERROR, codes::error::INVALID_ROOM]); + stream + .write_all(&[codes::ERROR, codes::error::INVALID_ROOM]) + .unwrap(); } else { - stream.write_all(&[codes::RESPONSE_OK]); + stream.write_all(&[codes::RESPONSE_OK]).unwrap(); } } None => { - stream.write_all(&[codes::ERROR, codes::error::INVALID_ROOM]); + stream + .write_all(&[codes::ERROR, codes::error::INVALID_ROOM]) + .unwrap(); } } } pub fn start() { let listener: TcpListener = TcpListener::bind(SERVER_ADDRESS).expect("Failed to bind to port"); + // let incoming: &std::net::Incoming = &listener.incoming(); let server: Arc> = Arc::new(Mutex::new(Server::new())); let server_outer: Arc> = Arc::clone(&server); println!("Server listening on {}", SERVER_ADDRESS); @@ -174,18 +196,48 @@ pub fn start() { let mut buf_in: [u8; 1024] = [0; 1024]; let server_inner: Arc> = Arc::clone(&server_outer); - thread::spawn(move || loop { + thread::spawn(move || { + let nickname: String; match stream.read(&mut buf_in) { Ok(size) => { let cmd_bytes: &[u8] = &buf_in[0..1]; let param_bytes: &[u8] = &buf_in[1..size]; + if cmd_bytes[0] == codes::client::REGISTER_NICK { + nickname = String::from_utf8_lossy(param_bytes).to_string(); + register_nick(&server_inner, &nickname, &mut stream); + loop { + match stream.read(&mut buf_in) { + Ok(size) => { + let cmd_bytes: &[u8] = &buf_in[0..1]; + let param_bytes: &[u8] = &buf_in[1..size]; - handle_client(&server_inner, &mut stream, cmd_bytes, param_bytes); + handle_client( + &server_inner, + &mut stream, + &nickname, + cmd_bytes, + param_bytes, + ); + } + Err(_) => { + eprintln!("Error parsing client"); + stream.write(&[codes::END]).unwrap(); + break; + } + } + } + } else { + stream + .write_all(&[ + codes::ERROR, + codes::error::NOT_YET_REGISTERED, + ]) + .unwrap(); + } } Err(_) => { eprintln!("Error parsing client"); - stream.write(&[codes::END]); - break; + stream.write(&[codes::END]).unwrap(); } } }); @@ -204,7 +256,7 @@ pub fn start() { let inp: String = input!(":"); match inp.parse::() { Ok(num) => match num { - 0 => break, + 0 => {println!("Goodbye"); }, 1 => println!("Users: {:?}", server.lock().unwrap().users), 2 => println!("Rooms: {:?}", server.lock().unwrap().rooms), _ => println!("Invalid Input"),