client and server refactor

This commit is contained in:
David Westgate 2023-11-25 21:09:05 -08:00
parent 9f1f23623d
commit bdf70ea8d5
3 changed files with 142 additions and 102 deletions

View File

@ -4,6 +4,40 @@ use std::io::{Read, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::thread; 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<u8> = 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<u8> = 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) { fn read_messages(mut stream: TcpStream) {
let mut buffer: [u8; 1024] = [0; 1024]; let mut buffer: [u8; 1024] = [0; 1024];
loop { loop {
@ -64,67 +98,8 @@ fn process_message(msg_bytes: &[u8]) {
fn disconnect() {} 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<u8> = 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 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<u8> = 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<u8> = 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() { pub fn start() {
println!("Starting the IRC client. No spaces allowed in nicknames or room names"); println!("Starting the IRC client. No spaces allowed in nicknames or room names");
let mut nick: String; let mut nick: String;
@ -155,32 +130,42 @@ pub fn start() {
for i in 1..nick.len() + 1 { for i in 1..nick.len() + 1 {
buf[i] = *nick.as_bytes().get(i - 1).unwrap(); buf[i] = *nick.as_bytes().get(i - 1).unwrap();
} }
stream.write(&buf); stream.write(&buf).unwrap();
loop { loop {
let inp: String = input!(":"); let inp: String = input!(":");
let cmds: Vec<_> = inp.split(" ").collect(); let cmds: Vec<_> = inp.split(" ").collect();
match *cmds.get(0).unwrap() { match *cmds.get(0).unwrap() {
"/quit" => disconnect(), "/quit" => disconnect(),
"/rooms" => rooms(&mut stream), "/rooms" => no_param_op(codes::client::LIST_ROOMS, &mut stream),
"/users" => users(&mut stream), "/users" => no_param_op(codes::client::LIST_USERS, &mut stream),
"/list" => { "/list" => {
let room = *cmds.get(1).unwrap(); let room = *cmds.get(1).unwrap();
list(room, &mut stream); one_param_op(codes::client::LIST_USERS_IN_ROOM, &mut stream, room);
} }
"/join" => { "/join" => {
let room = *cmds.get(1).unwrap(); 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), "/show" => show(&mut stream),
"/leave" => { "/leave" => {
let room = *cmds.get(1).unwrap(); let room: &str = *cmds.get(1).unwrap();
leave(&nick, room, &mut stream) 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 { } else {

View File

@ -9,6 +9,7 @@ pub mod codes {
pub const REGISTER_NICK: u8 = 0x06; pub const REGISTER_NICK: u8 = 0x06;
pub const LIST_USERS: u8 = 0x07; pub const LIST_USERS: u8 = 0x07;
pub const LIST_USERS_IN_ROOM: u8 = 0x08; pub const LIST_USERS_IN_ROOM: u8 = 0x08;
pub const SEND_MESSAGE_TO_ROOM: u8 = 0x09;
} }
pub const QUIT: u8 = 0x0B; pub const QUIT: u8 = 0x0B;
pub const KEEP_ALIVE: u8 = 0x0C; pub const KEEP_ALIVE: u8 = 0x0C;
@ -20,5 +21,7 @@ pub mod codes {
pub const INVALID_ROOM: u8 = 0x10; pub const INVALID_ROOM: u8 = 0x10;
pub const NICKNAME_COLLISION: u8 = 0x11; pub const NICKNAME_COLLISION: u8 = 0x11;
pub const SERVER_FULL: u8 = 0x12; pub const SERVER_FULL: u8 = 0x12;
pub const ALREADY_REGISTERED: u8 = 0x13;
pub const NOT_YET_REGISTERED: u8 = 0x14;
} }
} }

View File

@ -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( fn handle_client(
server: &Arc<Mutex<Server>>, server: &Arc<Mutex<Server>>,
stream: &mut TcpStream, stream: &mut TcpStream,
nickname: &str,
cmd_bytes: &[u8], cmd_bytes: &[u8],
param_bytes: &[u8], param_bytes: &[u8],
) { ) {
// handle user commands // handle user commands
match cmd_bytes[0] { match cmd_bytes[0] {
codes::client::REGISTER_NICK => { codes::client::REGISTER_NICK => {
let nickname: String = String::from_utf8_lossy(param_bytes).to_string(); stream
register_nick(server, nickname, stream); .write_all(&[codes::ERROR, codes::error::ALREADY_REGISTERED])
.unwrap();
} }
codes::client::LIST_ROOMS => { codes::client::LIST_ROOMS => {
let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let mut buf_out: Vec<u8> = Vec::new(); let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]); 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(room.as_bytes());
buf_out.extend_from_slice(&[0x20]); buf_out.extend_from_slice(&[0x20]);
} }
stream.write(&buf_out); stream.write(&buf_out).unwrap();
} }
codes::client::LIST_USERS => { codes::client::LIST_USERS => {
let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap();
let mut buf_out: Vec<u8> = Vec::new(); let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]); 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(user.as_bytes());
buf_out.extend_from_slice(&[0x20]); buf_out.extend_from_slice(&[0x20]);
} }
stream.write(&buf_out); stream.write(&buf_out).unwrap();
} }
codes::client::LIST_USERS_IN_ROOM => { codes::client::LIST_USERS_IN_ROOM => {
@ -67,15 +82,17 @@ fn handle_client(
let mut buf_out: Vec<u8> = Vec::new(); let mut buf_out: Vec<u8> = Vec::new();
buf_out.extend_from_slice(&[codes::RESPONSE]); buf_out.extend_from_slice(&[codes::RESPONSE]);
match unlocked_server.rooms.get(&room) { match unlocked_server.rooms.get(&room) {
Some(l) =>{ Some(l) => {
for ele in l { for ele in l {
buf_out.extend_from_slice(ele.as_bytes()); buf_out.extend_from_slice(ele.as_bytes());
buf_out.extend_from_slice(&[0x20]); buf_out.extend_from_slice(&[0x20]);
} }
stream.write_all(&buf_out); stream.write_all(&buf_out).unwrap();
}, }
None =>{ None => {
stream.write_all(&[codes::ERROR, codes::error::INVALID_ROOM]); stream
.write_all(&[codes::ERROR, codes::error::INVALID_ROOM])
.unwrap();
} }
} }
} }
@ -83,17 +100,15 @@ fn handle_client(
codes::client::JOIN_ROOM => { codes::client::JOIN_ROOM => {
let p: String = String::from_utf8_lossy(param_bytes).to_string(); let p: String = String::from_utf8_lossy(param_bytes).to_string();
let params: Vec<&str> = p.split(' ').collect(); let params: Vec<&str> = p.split(' ').collect();
let user = params.get(0).unwrap(); let room = params.get(0).unwrap();
let room = params.get(1).unwrap(); join_room(server, &nickname, room, stream)
join_room(server, *user, room, stream)
} }
codes::client::LEAVE_ROOM => { codes::client::LEAVE_ROOM => {
let p: String = String::from_utf8_lossy(param_bytes).to_string(); let p: String = String::from_utf8_lossy(param_bytes).to_string();
let params: Vec<&str> = p.split(' ').collect(); let params: Vec<&str> = p.split(' ').collect();
let user = params.get(0).unwrap(); let room = params.get(0).unwrap();
let room = params.get(1).unwrap(); leave_room(server, &nickname, room, stream)
leave_room(server, user, room, stream)
} }
codes::client::SEND_MESSAGE => { codes::client::SEND_MESSAGE => {
@ -109,19 +124,21 @@ fn handle_client(
// } // }
} }
fn register_nick(server: &Arc<Mutex<Server>>, nickname: String, stream: &mut TcpStream) { fn register_nick(server: &Arc<Mutex<Server>>, nickname: &str, stream: &mut TcpStream) {
// Check for nickname collision // Check for nickname collision
let mut unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); 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)] #[cfg(debug_assertions)]
println!("Nickname Collision, {}", nickname); println!("Nickname Collision, {}", nickname);
stream.write_all(&[codes::ERROR, codes::error::NICKNAME_COLLISION]); stream
.write_all(&[codes::ERROR, codes::error::NICKNAME_COLLISION])
.unwrap();
} else { } else {
// Add the user to the user list // Add the user to the user list
unlocked_server.users.insert(nickname.clone()); unlocked_server.users.insert(nickname.to_string());
// Send response ok // 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<Mutex<Server>>, user: &str, room: &str, stream: &mut T
unlocked_server.rooms.insert(room.to_string(), list); 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<Mutex<Server>>, user: &str, room: &str, stream: &mut TcpStream) { fn leave_room(server: &Arc<Mutex<Server>>, user: &str, room: &str, stream: &mut TcpStream) {
@ -148,21 +165,26 @@ fn leave_room(server: &Arc<Mutex<Server>>, user: &str, room: &str, stream: &mut
l.retain(|item| item != user); l.retain(|item| item != user);
if l.len() == 0 { if l.len() == 0 {
unlocked_server.rooms.remove(room); unlocked_server.rooms.remove(room);
stream.write_all(&[codes::RESPONSE_OK]); stream.write_all(&[codes::RESPONSE_OK]).unwrap();
} else if l.len() == before_len { } 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 { } else {
stream.write_all(&[codes::RESPONSE_OK]); stream.write_all(&[codes::RESPONSE_OK]).unwrap();
} }
} }
None => { None => {
stream.write_all(&[codes::ERROR, codes::error::INVALID_ROOM]); stream
.write_all(&[codes::ERROR, codes::error::INVALID_ROOM])
.unwrap();
} }
} }
} }
pub fn start() { pub fn start() {
let listener: TcpListener = TcpListener::bind(SERVER_ADDRESS).expect("Failed to bind to port"); let listener: TcpListener = TcpListener::bind(SERVER_ADDRESS).expect("Failed to bind to port");
// let incoming: &std::net::Incoming = &listener.incoming();
let server: Arc<Mutex<Server>> = Arc::new(Mutex::new(Server::new())); let server: Arc<Mutex<Server>> = Arc::new(Mutex::new(Server::new()));
let server_outer: Arc<Mutex<Server>> = Arc::clone(&server); let server_outer: Arc<Mutex<Server>> = Arc::clone(&server);
println!("Server listening on {}", SERVER_ADDRESS); println!("Server listening on {}", SERVER_ADDRESS);
@ -174,18 +196,48 @@ pub fn start() {
let mut buf_in: [u8; 1024] = [0; 1024]; let mut buf_in: [u8; 1024] = [0; 1024];
let server_inner: Arc<Mutex<Server>> = Arc::clone(&server_outer); let server_inner: Arc<Mutex<Server>> = Arc::clone(&server_outer);
thread::spawn(move || loop { thread::spawn(move || {
let nickname: String;
match stream.read(&mut buf_in) { match stream.read(&mut buf_in) {
Ok(size) => { Ok(size) => {
let cmd_bytes: &[u8] = &buf_in[0..1]; let cmd_bytes: &[u8] = &buf_in[0..1];
let param_bytes: &[u8] = &buf_in[1..size]; 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(_) => { Err(_) => {
eprintln!("Error parsing client"); eprintln!("Error parsing client");
stream.write(&[codes::END]); stream.write(&[codes::END]).unwrap();
break;
} }
} }
}); });
@ -204,7 +256,7 @@ pub fn start() {
let inp: String = input!(":"); let inp: String = input!(":");
match inp.parse::<u8>() { match inp.parse::<u8>() {
Ok(num) => match num { Ok(num) => match num {
0 => break, 0 => {println!("Goodbye"); },
1 => println!("Users: {:?}", server.lock().unwrap().users), 1 => println!("Users: {:?}", server.lock().unwrap().users),
2 => println!("Rooms: {:?}", server.lock().unwrap().rooms), 2 => println!("Rooms: {:?}", server.lock().unwrap().rooms),
_ => println!("Invalid Input"), _ => println!("Invalid Input"),