diff --git a/.gitignore b/.gitignore index 9430d83..5126b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ /target **/*.rs.bk *.lock - -Lucien-Rust/target/ +*~ +Rust/target/ diff --git a/Lucien-Rust/Cargo.toml b/Lucien-Rust/Cargo.toml deleted file mode 100644 index e4201bc..0000000 --- a/Lucien-Rust/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "chat-reseau" -version = "0.1.0" -authors = ["Phuntsok Drak-pa "] - -[dependencies] diff --git a/Lucien-Rust/src/main.rs b/Lucien-Rust/src/main.rs deleted file mode 100644 index 8cf7aea..0000000 --- a/Lucien-Rust/src/main.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::env; -use std::net::{TcpListener, TcpStream}; -use std::thread; -#[allow(unused_imports)] -use std::io::{stdin, stdout, Read, Write}; -#[allow(unused_imports)] -use std::sync::{Arc, Mutex}; - -/////////////////////////////////////////////////////////////////////////////// -// // -// Client // -// // -/////////////////////////////////////////////////////////////////////////////// - -fn get_entry() -> String { - let mut buf = String::new(); - - stdin().read_line(&mut buf).unwrap(); - buf.replace("\n", "").replace("\r", "") -} - -fn exchange_with_server(mut stream: TcpStream) { - let stdout = std::io::stdout(); - let mut io = stdout.lock(); - let buf = &mut [0; 3]; - - println!("Enter `quit` when you want to leave"); - loop { - write!(io, "> ").unwrap(); - io.flush().unwrap(); - match &*get_entry() { - "quit" => { - println!("bye!"); - return; - } - "exit" => { - println!("bye!"); - return; - } - line => { - write!(stream, "{}\n", line).unwrap(); - match stream.read(buf) { - Ok(received) => { - if received < 1 { - println!("Perte de la connexion avec le serveur"); - return; - } - } - Err(_) => { - println!("Perte de la connexion avec le serveur"); - return; - } - } - // println!("Réponse du serveur : {:?}", buf); - let reponse = String::from_utf8(buf.to_vec()).unwrap(); - println!("Réponse du serveur : {}", reponse); - } - } - } -} - -fn client(server_address: String) { - println!("Tentative de connexion a serveur..."); - match TcpStream::connect(server_address) { - Ok(stream) => { - println!("Connexion au serveur réussie !"); - exchange_with_server(stream); - } - Err(e) => { - println!("La connection au serveur a échoué : {}", e); - } - } -} - -/////////////////////////////////////////////////////////////////////////////// -// // -// Server // -// // -/////////////////////////////////////////////////////////////////////////////// - -fn handle_client(mut stream: &TcpStream, adresse: &str) { - let mut msg: Vec = Vec::new(); - loop { - let buf = &mut [0; 10]; - - match stream.read(buf) { - Ok(received) => { - // si on a reçu 0 octet, ça veut dire que le client s'est déconnecté - if received < 1 { - println!("Client disconnected {}", adresse); - return; - } - let mut x = 0; - - for c in buf { - // si on a dépassé le nombre d'octets reçus, inutile de continuer - if x >= received { - break; - } - x += 1; - if *c == '\n' as u8 { - println!( - "message reçu {} : {}", - adresse, - // on convertit maintenant notre buffer en String - String::from_utf8(msg).unwrap() - ); - - stream.write(b"ok\n").unwrap(); - - msg = Vec::new(); - } else { - msg.push(*c); - } - } - } - Err(_) => { - println!("Client disconnected {}", adresse); - return; - } - } - } -} - -fn serveur(port: String) { - println!("Port: {}", port); - let mut serv = String::from("127.0.0.1:"); - serv.push_str(&port); - let listener = TcpListener::bind(serv.to_string()).unwrap(); - - println!("En attente d’un client..."); - - // Multi-client /////////////////////////////////////////////////////////// - for stream in listener.incoming() { - match stream { - Ok(stream) => { - let adresse = match stream.peer_addr() { - Ok(addr) => format!("[adresse : {}]", addr), - Err(_) => "inconnue".to_owned(), - }; - - println!("Nouveau client {}", adresse); - thread::spawn(move || handle_client(&stream, &*adresse)); - } - Err(e) => { - println!("La connexion du client a échoué : {}", e); - } - } - println!("En attente d’un autre client..."); - } -} - -fn main() { - let args: Vec = env::args().collect(); - if args.len() == 2 { - /////////////////////////////////////////////////////////////////////// - // Server opened // - /////////////////////////////////////////////////////////////////////// - println!("Opening server on port {}", args[1]); - serveur(args[1].clone()); - } else if args.len() == 3 { - /////////////////////////////////////////////////////////////////////// - // Client opened // - /////////////////////////////////////////////////////////////////////// - println!("Client connecting on server {}:{}", args[1], args[2]); - let mut serv = if args[1] == String::from("localhost") { - String::from("127.0.0.1") - } else { - args[1].clone() - }; - serv.push(':'); - serv.push_str(&args[2]); - client(serv); - } else { - println!("Usage: {} [server ip] port", args[0]); - } -} diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml new file mode 100644 index 0000000..9b090f2 --- /dev/null +++ b/Rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chat-reseau" +version = "0.5.7" +authors = ["Lucien Cartier-Tilet "] + +[dependencies] +colored = "1.6" +chrono = "0.4" +term_size = "0.3" diff --git a/Rust/src/client.rs b/Rust/src/client.rs new file mode 100644 index 0000000..79d7449 --- /dev/null +++ b/Rust/src/client.rs @@ -0,0 +1,353 @@ +extern crate chrono; +extern crate colored; +extern crate term_size; +use std; +use std::io::*; +use std::net::TcpStream; +use std::thread; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use self::colored::*; +use self::chrono::Local; + +/* + +0.1 [X] +1.1 [X] +1.2 [ ] +1.3 [X] +1.4 [X] +1.5 [X] +1.6 [X] +1.7 [X] +1.8 [X] +1.9 [X] +2.1 [X] +2.2 [X] +3.1 [ ] // pas utile avec Rust +3.2 [X] +4.1.1 [X] +4.1.2 [X] +4.2.1 [X] +4.2.2 [X] + + */ + +// TODO: Implement requests 1.2 + +fn hash_name(name: &str) -> usize { + let mut s = DefaultHasher::new(); + let name = String::from(name); + name.hash(&mut s); + s.finish() as usize +} + +fn get_time() -> String { + let date = Local::now(); + date.format("[%H:%M:%S]").to_string() +} + +fn print_line(name: &str, name_hash: usize, msg: &str, first_line: &mut bool, colors: &Vec<&str>) { + let date = get_time(); + if *first_line == true { + println!( + "{}{}{}{}", + date.dimmed(), + name.color(colors[name_hash]), + " |".green(), + msg.yellow().dimmed() + ); + *first_line = false; + } else { + println!( + "{}{}", + " | ".green(), + msg.yellow().dimmed() + ); + } +} + +fn get_entry() -> String { + let mut buf = String::new(); + stdin().read_line(&mut buf).unwrap(); + buf.replace("\n", "").replace("\r", "") +} + +fn get_name() -> String { + 'mainloop: loop { + println!("{}", "Please enter your name:".yellow().dimmed()); + let mut name = &*get_entry(); + name = name.trim(); + if name.len() > 20 { + println!( + "{}", + "Nickname too long, it must be at most 20 characters long".red() + ); + continue; + } + for c in name.chars() { + if !c.is_ascii() { + println!( + "{}{}{}", + "Character ".red(), + &format!("{}", c).green(), + " is not an ASCII character.".red() + ); + continue 'mainloop; + } + } + match name { + "" => { + continue; + } + _ => { + let spliced_name: Vec<&str> = name.split_whitespace().collect(); + if spliced_name.len() != 1 { + println!("{}", "Cannot use whitespace in name".red()); + continue; + } + return String::from(name); + } + } + } +} + +fn write_to_server(stream: TcpStream) { + let mut writer = BufWriter::new(&stream); + writeln!(writer, "REQ CLIENTS").unwrap(); + writer.flush().unwrap(); + loop { + let line = &*get_entry(); + line.trim(); + match line { + "" => {} + "/quit" => { + writeln!(writer, "BYE").unwrap(); + writer.flush().unwrap(); + return (); + } + "/clients" => { + writeln!(writer, "REQ CLIENTS").unwrap(); + writer.flush().unwrap(); + } + line => { + if line.len() > 2000 { + println!( + "{}", + "Cannot send a message longer than 2000 characters".bright_red() + ); + continue; + } + writeln!(writer, "MSG {}", line).unwrap(); + writer.flush().unwrap(); + } + } + } +} + +fn exchange_with_server(stream: TcpStream) { + let server = stream.peer_addr().unwrap(); + println!("Connected to {}", server); + + #[allow(non_snake_case)] + let COLORS: Vec<&str> = vec![ + // "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + // "white", + ]; + + let stream_cpy = stream.try_clone().unwrap(); + let mut reader = BufReader::new(&stream_cpy); + let mut writer = BufWriter::new(&stream_cpy); + + macro_rules! receive { + () => ({ + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(len) => { + if len == 0 { + // Reader is at EOF. Could use ErrorKind::UnexpectedEOF, + // but still unstable. + let ret = std::io::Error::new( + std::io::ErrorKind::Other, "test"); + return + Err(ret): std::result::Result<&str,std::io::Error>; + } + line.pop(); + } + Err(e) => { return Err(e); } + }; + line + }) + } + + // entrée du nom d'utilisateur + writeln!(writer, "PROT {} CONNECT NEW", ::PROTOCOL).unwrap(); + writer.flush().unwrap(); + let _name: String = match (|| loop { + let _answer = receive!(); + if _answer != "NAME REQ" { + return Err(Error::new(ErrorKind::Other, _answer)); + } + let nick = get_name(); + writeln!(writer, "NAME {}", nick).unwrap(); + writer.flush().unwrap(); + match receive!().as_str() { + "NAME OK" => { + println!("NAME OK"); + nick + } + + "NAME FAILURE" => { + println!("{}", "Username refused by server.".red()); + continue; + } + answer => { + println!("{}{}", "Server answered: ".yellow().dimmed(), answer); + return Err(Error::new(ErrorKind::Other, answer)); + } + }; + })() + { + Ok(name) => String::from(name), + Err(_) => { + println!("{}", ">>> Login successful".green()); + String::new() + } + }; + + thread::spawn(move || { + write_to_server(stream.try_clone().unwrap()); + }); + + match (|| loop { + let input: String = String::from(receive!()); + let spliced_input: Vec<&str> = input.split(" ").collect(); + match spliced_input[0] { + "BAD" => { + println!("{}", "Bad request from client".red()); + } + "WELCOME" => println!( + "{}\n{}\n{}", + ">>> Login Successful <<<".green(), + "Type /clients to get the list of users connected", + "Type /quit to disconnect and quit" + ), + + "FROM" => { + let date = Local::now(); + let mut first_line = true; + let name_hash = hash_name(spliced_input[1]) % COLORS.len(); + + // Formatting name + let mut name = String::new(); + for _i in 0..(20 - spliced_input[1].to_string().len()) { + name.push(' '); + } + name.push('<'); + name.push_str(spliced_input[1]); + name.push('>'); + + // Display message with soft-wrap + if let Some((w, _)) = term_size::dimensions() { + let mut msg = String::new(); + let w = w - 34; + + // format message + for mut i in 3..spliced_input.len() { + // If the width of the terminal allows the length of the + // current width of the message plus the new word, then + // add the latter, otherwise print the former and create + // a new line from the current message + if w > msg.len() + spliced_input[i].len() + 1 { + msg.push(' '); + msg.push_str(spliced_input[i]); + } else { + print_line(&name, name_hash, &msg, &mut first_line, &COLORS); + msg = String::from(spliced_input[i]); + } + } + + // print leftovers + print_line(&name, name_hash, &msg, &mut first_line, &COLORS); + } else { + let mut msg = String::new(); + for i in 3..spliced_input.len() { + msg.push_str(" "); + msg.push_str(spliced_input[i]); + } + println!( + "{}{}{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + name.color(COLORS[name_hash]), + msg.yellow().dimmed() + ); + } + } + "BYE" => return Ok("Ok"), + + "LIST" => { + println!( + "{}{}{}", + ">>>> LIST OF CLIENTS CONNECTED (".bold().yellow(), + spliced_input[2], + ") <<<<".bold().yellow() + ); + for i in 3..spliced_input.len() { + println!("\t\t{}", spliced_input[i]); + } + } + "JOIN" => { + let date = get_time(); + let name_hash: usize = hash_name(spliced_input[1]) % COLORS.len(); + + println!( + "{}{}{}{}", + date.dimmed(), + " ------> ".green(), + spliced_input[1].color(COLORS[name_hash]), + " has joined".green() + ) + } + "LOGOUT" => { + let date = get_time(); + let name_hash = hash_name(spliced_input[1]) % COLORS.len(); + + println!( + "{}{}{}{}", + date.dimmed(), + " <------ ".red(), + spliced_input[1].color(COLORS[name_hash]), + " has left".red() + ) + } + _ => println!("{}", input), + } + })() + { + Ok(_) => { + println!("{}", ">>> Logout successful <<<".green()); + } + Err(_) => { + println!("{}", "Error: Connection with server lost".red()); + } + } +} + +pub fn client(server_address: String) { + println!("Trying to connect to the server..."); + match TcpStream::connect(server_address) { + Ok(stream) => { + exchange_with_server(stream); + } + Err(e) => { + println!("{} {}", "Connection to server failed:".red(), e); + return; + } + } +} diff --git a/Rust/src/main.rs b/Rust/src/main.rs new file mode 100644 index 0000000..cb13cb9 --- /dev/null +++ b/Rust/src/main.rs @@ -0,0 +1,37 @@ +#![feature(type_ascription)] +#![feature(stmt_expr_attributes)] +use std::env; + +static PROTOCOL: &'static str = "0.1"; + +pub mod client; +pub mod server; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() == 2 { + /////////////////////////////////////////////////////////////////////// + // Server opened // + /////////////////////////////////////////////////////////////////////// + println!("Opening server on port {}", args[1]); + // serveur(args[1].clone()); + let mut serv = String::from("0.0.0.0:"); + serv.push_str(&args[1]); + server::serveur(serv); + } else if args.len() == 3 { + /////////////////////////////////////////////////////////////////////// + // Client opened // + /////////////////////////////////////////////////////////////////////// + println!("Client connecting on server {}:{}", args[1], args[2]); + let mut serv: String = if args[1] == "localhost" { + String::from("127.0.0.1") + } else { + args[1].clone() + }; + serv.push(':'); + serv.push_str(&args[2]); + client::client(serv); + } else { + println!("Usage: {} [server ip] port", args[0]); + } +} diff --git a/Rust/src/server.rs b/Rust/src/server.rs new file mode 100644 index 0000000..9edc66c --- /dev/null +++ b/Rust/src/server.rs @@ -0,0 +1,478 @@ +extern crate chrono; +use std::io::*; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::thread; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::collections::HashMap; +use self::chrono::Local; + +// TODO: add server-side controls: display clients list, kick client, shutdown + +/////////////////////////////////////////////////////////////////////////////// +// Evolution implementation protocole // +/////////////////////////////////////////////////////////////////////////////// + +/* + +0.1 [X] +1.1 [X] +1.2 [X] +1.3 [X] +1.4 [X] +1.5 [X] +1.6 [X] +1.7 [X] +1.8 [X] +1.9 [X] +2.1 [X] +2.2 [X] +3.1 [ ] // pas utile avec Rust +3.2 [X] +4.1.1 [X] +4.1.2 [X] +4.2.1 [X] +4.2.2 [X] + +*/ + +/////////////////////////////////////////////////////////////////////////////// +// TYPES // +/////////////////////////////////////////////////////////////////////////////// + +// Map for all connected clients containing their name and stream +type UserMapValue = (String, TcpStream); +type UserMap = HashMap; + +/////////////////////////////////////////////////////////////////////////////// +// CODE // +/////////////////////////////////////////////////////////////////////////////// + +fn get_time() -> String { + let date = Local::now(); + date.format("[%H:%M:%S]").to_string() +} + +fn distribute_message( + msg: &str, + not_to: &SocketAddr, + lock: &mut MutexGuard, + everyone: bool, +) { + let mut name = String::new(); + for (client, entry) in (*lock).iter() { + if client == not_to { + name = entry.0.clone(); + break; + } + } + for (other_client, entry) in (*lock).iter() { + let other_name = &entry.0; + let other_stream = &entry.1; + if everyone == false && other_client == not_to { + continue; + } + match (|| -> Result<()> { + let mut writer = BufWriter::new(other_stream); + // test if message begins with "MSG " ///////////////////////// + if &msg[..4] == "MSG " { + try!(writeln!(writer, "FROM {} {}", name, msg)); + } else { + try!(writeln!(writer, "{}", msg)); + } + /////////////////////////////////////////////////////////////// + try!(writer.flush()); + return Ok(()); + })() + { + Ok(_) => {} + Err(e) => { + println!( + "{} Client {} <{}> disappeared during message distribution: {}", + get_time(), + other_client, + other_name, + e + ); + } + } + } +} + +fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard) { + let mut clients = String::new(); + let mut num_client = 0usize; + for (client, entry) in (*lock).iter() { + num_client += 1; + clients.push_str(&format!( + "{}{} ", + &entry.0.trim(), + if client == to { "(you)" } else { "" } + )); + } + let clients = clients.trim(); + for (client, entry) in (*lock).iter() { + if client == to { + let stream = &entry.1; + let mut writer = BufWriter::new(stream); + let mut req = String::from(format!("{}{} ", "LIST CLIENTS ", num_client)); + req.push_str(clients); + println!( + "{time} to {nick}@{addr} : {message}", + time = get_time(), + nick = &entry.0, + addr = &entry.1.peer_addr().unwrap(), + message = req + ); + writeln!(writer, "{}", req).unwrap(); + writer.flush().unwrap(); + return; + } + } +} + +fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard) { + (*lock).remove(&client); + distribute_message(&format!("LOGOUT {}", name), client, lock, true); +} + +fn handle_client(stream: TcpStream, clients: Arc>) { + // Get client IP and port + let client = stream.peer_addr().unwrap(); + println!("{} New connection from {}", get_time(), client); + + // Buffered reading and writing + let mut reader = BufReader::new(&stream); + let mut writer = BufWriter::new(&stream); + + // Write an entire line to the client + // Can fail on IO errors, du to try! macro + macro_rules! send { + ($line:expr) => ({ + try!(writeln!(writer, "{}", $line)); + try!(writer.flush()); + }) + } + + // Read an entire line from the client + // Can fail on IO errors or when EOF is reached + macro_rules! receive { + () => ({ + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(len) => { + if len == 0 { + // Reader is at EOF. + return Err(Error::new(ErrorKind::Other, "unexpected EOF")); + } + line.pop(); + } + Err(e) => { + return Err(e); + } + }; + line + }) + } + + // Get user's name + let name: String = match (|| loop { + match receive!() { + input => { + println!( + "{time} Client {addr} : {message}", + time = get_time(), + addr = client, + message = input + ); + let spliced_input: Vec<&str> = input.split_whitespace().collect(); + if spliced_input.len() != 4 && spliced_input.len() != 5 + || spliced_input[0] != "PROT" + { + return Err(Error::new(ErrorKind::Other, "BAD REQ")); + } + if spliced_input[1] != ::PROTOCOL { + return Err(Error::new(ErrorKind::Other, "BAD PROT")); + } + if spliced_input.len() == 5 { + if spliced_input[2] == "CONNECT" && spliced_input[3] == "USER" { + let username = String::from(spliced_input[4]); + let mut ascii_nick = true; + for c in username.chars() { + if !c.is_ascii() { + ascii_nick = false; + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE" + ); + send!("NAME FAILURE"); + break; + } + } + if ascii_nick { + let mut used = false; + { + let lock = clients.lock().unwrap(); + for (_, entry) in (*lock).iter() { + if username == entry.0 { + used = true; + break; + } + } + } + if used == false { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME OK" + ); + send!("NAME OK"); + return Ok(username); + } else { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE" + ); + send!("NAME FAILURE"); + } + } + } else { + return Err(Error::new(ErrorKind::Other, "BAD REQ")); + } + } + + loop { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME REQ" + ); + send!("NAME REQ"); + match receive!() { + input => { + println!( + "{time} Client {addr} : {message}", + time = get_time(), + addr = client, + message = input + ); + let spliced_input: Vec<&str> = input.split_whitespace().collect(); + if spliced_input.len() != 2 || spliced_input[0] != "NAME" { + return Err(Error::new(ErrorKind::Other, "BAD REQ")); + } + let username = String::from(spliced_input[1]); + let mut ascii_nick = true; + for c in username.chars() { + if !c.is_ascii() { + ascii_nick = false; + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE" + ); + send!("NAME FAILURE"); + break; + } + } + if ascii_nick { + let mut used = false; + { + let lock = clients.lock().unwrap(); + for (_, entry) in (*lock).iter() { + if username == entry.0 { + used = true; + break; + } + } + } + if used == false { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME OK" + ); + send!("NAME OK"); + return Ok(username); + } else { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE" + ); + send!("NAME FAILURE"); + } + } + } + } + } + } + } + })() + { + Ok(name) => name, + Err(e) => { + println!( + "{time} client {addr} encountered an error: {err}", + time = get_time(), + addr = client, + err = e + ); + writeln!(writer, "{}", e).unwrap(); + writer.flush().unwrap(); + return (); + } + }; + + // Add user to global map. Lock will be released at the end of the scope + { + let mut lock = clients.lock().unwrap(); + (*lock).insert(client, (name.clone(), stream.try_clone().unwrap())); + distribute_message(&format!("JOIN {}", name), &client, &mut lock, false); + } + + writeln!(writer, "WELCOME").unwrap(); + writer.flush().unwrap(); + + // Chat loop: Receive messages from users once connected + match (|| loop { + match receive!().as_str() { + input => { + println!( + "{time} {nick}@{addr}: {message}", + time = get_time(), + addr = client, + nick = name, + message = input + ); + + match input { + "BYE" => { + println!( + "{time} to {nick}@{addr} : {message}", + time = get_time(), + addr = client, + message = "BYE", + nick = name + ); + send!("BYE"); + return Ok(()); + } + + "PING" => { + println!( + "{time} to {nick}@{addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE", + nick = name + ); + send!("PONG"); + } + + "REQ CLIENTS" => { + let mut lock = clients.lock().unwrap(); + send_clients_name(&client, &mut lock); + } + input => { + let spliced_input: Vec<&str> = input.split_whitespace().collect(); + match spliced_input[0] { + "MSG" => { + let mut message = String::new(); + for i in 1..spliced_input.len() { + message.push_str(spliced_input[i]); + } + { + let mut lock = clients.lock().unwrap(); + distribute_message( + &format!("{}", input), + &client, + &mut lock, + true, + ); + } + } + _ => { + println!( + "{time} to client {addr} : \"{message}\", cause : {inmessage}", + time = get_time(), + addr = client, + message = "BAD REQ", + inmessage = input + ); + send!("BAD REQ"); + } + } + } + } + // { + // let mut lock = clients.lock().unwrap(); + // distribute_message(&format!("{}", input), &client, &mut lock, true); + // } + } + } + })() + { + Ok(_) => { + println!("{} Client {} <{}> left", get_time(), client, name); + } + Err(e) => { + println!( + "{} Client {} <{}> disappeared during chat: {}", + get_time(), + client, + name, + e + ); + } + } + + // Remove user from global map + { + let mut lock = clients.lock().unwrap(); + disconnect_user(&name, &client, &mut lock); + } +} + +pub fn serveur(addr: String) { + // Manage UserMap in a mutex + let clients = Arc::new(Mutex::new(HashMap::new())); + let serv_addr = addr.clone(); + + // Start a TCP Listener + let listener = match TcpListener::bind(serv_addr.as_str()) { + Ok(listener) => listener, + Err(e) => panic!("Could not read start TCP listener: {}", e), + }; + + println!( + "{} Successfully started the server on {}", + get_time(), + serv_addr + ); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + let clients = clients.clone(); + thread::spawn(move || { + //connection succeeded + handle_client(stream, clients) + }); + } + Err(e) => { + writeln!(stderr(), "Connection failed: {}", e).unwrap(); + } + } + } + + // close the socket server + drop(listener); +}