From 4291abbdafc275d3b08091dfd4dc436699d90472 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Mon, 26 Feb 2018 11:52:41 +0100 Subject: [PATCH 01/34] Small updates, little improvements --- .gitignore | 10 ++ Lucien-Rust/src/main.rs | 177 ------------------------ {Lucien-Rust => Lucien/Rust}/Cargo.toml | 0 Lucien/Rust/src/client.rs | 63 +++++++++ Lucien/Rust/src/main.rs | 27 ++++ Lucien/Rust/src/server.rs | 78 +++++++++++ 6 files changed, 178 insertions(+), 177 deletions(-) delete mode 100644 Lucien-Rust/src/main.rs rename {Lucien-Rust => Lucien/Rust}/Cargo.toml (100%) create mode 100644 Lucien/Rust/src/client.rs create mode 100644 Lucien/Rust/src/main.rs create mode 100644 Lucien/Rust/src/server.rs diff --git a/.gitignore b/.gitignore index 9430d83..dbbd374 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,13 @@ *.lock Lucien-Rust/target/ + +\.idea/ + +cmake-build-debug/ + +Lucien/Rust/target/ + +Lucien/Rust/Rust\.iml + +Lucien/server/ 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/Lucien-Rust/Cargo.toml b/Lucien/Rust/Cargo.toml similarity index 100% rename from Lucien-Rust/Cargo.toml rename to Lucien/Rust/Cargo.toml diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs new file mode 100644 index 0000000..0a7a0e1 --- /dev/null +++ b/Lucien/Rust/src/client.rs @@ -0,0 +1,63 @@ +use std::net::TcpStream; +use std::io::{Read, Write}; +use std::io::{stdin, stdout}; + +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 = stdout(); + let mut io = stdout.lock(); + let buf = &mut [0; 1024]; + + 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); + } + } + } +} + +pub 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); + } + } +} diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs new file mode 100644 index 0000000..ad25480 --- /dev/null +++ b/Lucien/Rust/src/main.rs @@ -0,0 +1,27 @@ +use std::env; + +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()); + server::serveur(args[1].clone()); + } else if args.len() == 3 { + /////////////////////////////////////////////////////////////////////// + // Client opened // + /////////////////////////////////////////////////////////////////////// + println!("Client connecting on server {}:{}", args[1], args[2]); + let mut serv = args[1].clone(); + serv.push(':'); + serv.push_str(&args[2]); + client::client(serv); + } else { + println!("Usage: {} [server ip] port", args[0]); + } +} diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs new file mode 100644 index 0000000..aa26b7a --- /dev/null +++ b/Lucien/Rust/src/server.rs @@ -0,0 +1,78 @@ +use std::net::{TcpListener, TcpStream}; +use std::io::{Read, Write}; +use std::thread; + +fn handle_client(mut stream: &TcpStream, adresse: &str, name: String) { + 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 {}({}) : {}", + name, + 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; + } + } + } +} + +pub 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(), + }; + + let name = String::from("Toto"); + + println!("Nouveau client {}", adresse); + thread::spawn(move || handle_client(&stream, &*adresse, name)); + } + Err(e) => { + println!("La connexion du client a échoué : {}", e); + } + } + println!("En attente d’un autre client..."); + } +} From ce45fb60ad4fe9d13accd8ea74ef7a7a4dbb979c Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Mon, 5 Mar 2018 17:39:11 +0100 Subject: [PATCH 02/34] Updated, but client still doesn't display text --- Lucien/Rust/Cargo.toml | 5 +- Lucien/Rust/src/client.rs | 109 ++++++++++++++++++++++++----- Lucien/Rust/src/main.rs | 4 +- Lucien/Rust/src/server.rs | 142 +++++++++++++++++++++----------------- 4 files changed, 174 insertions(+), 86 deletions(-) diff --git a/Lucien/Rust/Cargo.toml b/Lucien/Rust/Cargo.toml index e4201bc..7e36bbf 100644 --- a/Lucien/Rust/Cargo.toml +++ b/Lucien/Rust/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "chat-reseau" -version = "0.1.0" -authors = ["Phuntsok Drak-pa "] +version = "0.2.0" +authors = ["Lucien Cartier-Tilet "] [dependencies] +bufstream = "0.1" diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 0a7a0e1..1c5c328 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -1,6 +1,10 @@ +extern crate bufstream; use std::net::TcpStream; -use std::io::{Read, Write}; -use std::io::{stdin, stdout}; +use std::io::{stdin, stdout, Read, Write}; +// use std::sync::mpsc; +// use std::sync::mpsc::{Receiver, Sender}; +use std::thread::spawn; +// use self::bufstream::BufStream; fn get_entry() -> String { let mut buf = String::new(); @@ -9,12 +13,51 @@ fn get_entry() -> String { buf.replace("\n", "").replace("\r", "") } -fn exchange_with_server(mut stream: TcpStream) { +fn read_from_server( + mut stream: TcpStream, +) { + let buff = &mut [0; 1024]; let stdout = stdout(); let mut io = stdout.lock(); - let buf = &mut [0; 1024]; + loop { + match stream.read(buff) { + Ok(received) => { + if received < 1 { + // println!("Perte de connexion avec le serveur"); + write!(io, "Perte de connexion avec le serveur\n").unwrap(); + io.flush().unwrap(); + return; + } + } + Err(_) => { + // println!("Perte de connexion avec le serveur"); + write!(io, "Perte de connexion avec le serveur\n").unwrap(); + io.flush().unwrap(); + return; + } + } + let reponse = String::from_utf8(buff.to_vec()).unwrap(); + write!(io, "{}", reponse).unwrap(); + io.flush().unwrap(); + // println!("From server: {}", reponse); + } +} + +fn exchange_with_server( + mut stream: TcpStream +) { + let stdout = stdout(); + let mut io = stdout.lock(); + let _buff = &mut [0; 1024]; + + let stream_cpy = stream.try_clone().unwrap(); + spawn(move || { + // let stream_cpy = stream.try_clone().unwrap(); + read_from_server(stream_cpy); + }); + + println!("Enter `quit` or `exit` when you want to leave"); - println!("Enter `quit` when you want to leave"); loop { write!(io, "> ").unwrap(); io.flush().unwrap(); @@ -29,26 +72,53 @@ fn exchange_with_server(mut stream: TcpStream) { } 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; - } - } + // match stream.read(buff) { + // 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); + // let reponse = String::from_utf8(buf.to_vec()).unwrap(); + // println!("Réponse du serveur : {}", reponse); } } } } +// fn exchange_with_server(stream: TcpStream) { +// let (chan, recv): (Sender, Receiver) = mpsc::channel(); +// // let buf = &mut [0; 1024]; +// spawn(move || { +// loop { +// let msg = recv.recv().unwrap(); +// println!("{}", msg); +// } +// }); +// println!("Enter `quit` or `exit` when you want to leave"); +// loop { +// match &*get_entry() { +// "quit" => { +// println!("bye!"); +// return; +// } +// "exit" => { +// println!("bye!"); +// return; +// } +// line => { +// chan.send(format!("{}", line)).unwrap(); +// } +// } +// } +// } + pub fn client(server_address: String) { println!("Tentative de connexion a serveur..."); match TcpStream::connect(server_address) { @@ -58,6 +128,7 @@ pub fn client(server_address: String) { } Err(e) => { println!("La connection au serveur a échoué : {}", e); + return; } } } diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs index ad25480..7aa08a4 100644 --- a/Lucien/Rust/src/main.rs +++ b/Lucien/Rust/src/main.rs @@ -11,7 +11,9 @@ fn main() { /////////////////////////////////////////////////////////////////////// println!("Opening server on port {}", args[1]); // serveur(args[1].clone()); - server::serveur(args[1].clone()); + let mut serv = String::from("127.0.0.1:"); + serv.push_str(&args[1]); + server::serveur(serv); } else if args.len() == 3 { /////////////////////////////////////////////////////////////////////// // Client opened // diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index aa26b7a..7e358f4 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -1,78 +1,92 @@ -use std::net::{TcpListener, TcpStream}; -use std::io::{Read, Write}; -use std::thread; +extern crate bufstream; +use std::io::{BufRead, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::str::FromStr; +use std::sync::{mpsc, Arc, RwLock}; +use std::sync::mpsc::{Receiver, Sender}; +use std::thread::spawn; +use self::bufstream::BufStream; -fn handle_client(mut stream: &TcpStream, adresse: &str, name: String) { - let mut msg: Vec = Vec::new(); +fn handle_connection( + stream: &mut BufStream, + chan: Sender, + arc: Arc>>, +) { + stream.write(b"Welcome this server!\n").unwrap(); + stream + .write(b"Please input your username (max. 20chars): ") + .unwrap(); + stream.flush().unwrap(); + + let mut name = String::new(); + stream.read_line(&mut name).unwrap(); + let name = name.trim_right(); + stream + .write_fmt(format_args!("Hello, {}!\n", name)) + .unwrap(); + stream.flush().unwrap(); + + let mut pos = 0; 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 {}({}) : {}", - name, - 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; + { + let lines = arc.read().unwrap(); + for i in pos..lines.len() { + stream.write_fmt(format_args!("{}", lines[i])).unwrap(); + pos = lines.len(); } } + stream.write(b" > ").unwrap(); + stream.flush().unwrap(); + + let mut reads = String::new(); + stream.read_line(&mut reads).unwrap(); + if reads.trim().len() != 0 { + chan.send(format!("[{}] said: {}", name, reads)).unwrap(); + } } } -pub 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(); +pub fn serveur(addr: String) { + // Ouverture de la connexion sur socket + let addr = SocketAddr::from_str(&addr).unwrap(); + // Ajout d’un listener Tcp sur le socket + let listener = TcpListener::bind(addr).unwrap(); - println!("En attente d’un client..."); + // création des receveurs et envoyeurs de strings asynchrones + let (sender, receiver): (Sender, Receiver) = mpsc::channel(); + let arc: Arc>> = Arc::new(RwLock::new(Vec::new())); + let arc_w = arc.clone(); - // Multi-client /////////////////////////////////////////////////////////// + // boucle infinie en parallèle pour recevoir des messages + spawn(move || { + loop { + // lit le message depuis le receveur + let msg = receiver.recv().unwrap(); + print!("DEBUG: message {}", msg); + { + let mut arc_w = arc_w.write().unwrap(); + arc_w.push(msg); + } + } + }); + + // Réception des clients for stream in listener.incoming() { - match stream { - Ok(stream) => { - let adresse = match stream.peer_addr() { - Ok(addr) => format!("[adresse : {}]", addr), - Err(_) => "inconnue".to_owned(), - }; - - let name = String::from("Toto"); - - println!("Nouveau client {}", adresse); - thread::spawn(move || handle_client(&stream, &*adresse, name)); - } - Err(e) => { - println!("La connexion du client a échoué : {}", e); + match stream { + Err(e) => println!("Erreur écoute : {}", e), + Ok(mut stream) => { + println!( + "Nouvelle connexion de {} vers {}", + stream.peer_addr().unwrap(), + stream.local_addr().unwrap() + ); + let sender = sender.clone(); + let arc = arc.clone(); + spawn(move || { + let mut stream = BufStream::new(stream); + handle_connection(&mut stream, sender, arc); + }); } } - println!("En attente d’un autre client..."); } } From 288764df1eb772415f79bbc0b77ef8c929a04dd2 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 18 Mar 2018 21:07:20 +0100 Subject: [PATCH 03/34] server and client rewrite, issues with receive macro --- Lucien/Rust/src/client.rs | 137 ++++++++++++------------ Lucien/Rust/src/server.rs | 219 ++++++++++++++++++++++++++------------ 2 files changed, 215 insertions(+), 141 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 1c5c328..207865a 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -1,10 +1,19 @@ -extern crate bufstream; +use std::io::*; use std::net::TcpStream; -use std::io::{stdin, stdout, Read, Write}; -// use std::sync::mpsc; -// use std::sync::mpsc::{Receiver, Sender}; -use std::thread::spawn; -// use self::bufstream::BufStream; +use std::thread; + +static leave_msg: &str = "BYE"; + +// macro_rules! send { +// ($line:expr) => ({ +// try!(writeln!(writer, "{}", $line)); +// try!(writer.flush()); +// }) +// } + +fn send(writer: BufWriter<&TcpStream>,text: &str) { + +} fn get_entry() -> String { let mut buf = String::new(); @@ -13,81 +22,69 @@ fn get_entry() -> String { buf.replace("\n", "").replace("\r", "") } -fn read_from_server( - mut stream: TcpStream, -) { - let buff = &mut [0; 1024]; - let stdout = stdout(); - let mut io = stdout.lock(); +fn write_to_server(stream: TcpStream) { + + let mut writer = BufWriter::new(&stream); + loop { - match stream.read(buff) { - Ok(received) => { - if received < 1 { - // println!("Perte de connexion avec le serveur"); - write!(io, "Perte de connexion avec le serveur\n").unwrap(); - io.flush().unwrap(); - return; - } + match &*get_entry() { + "/quit" => { + println!("Disconnecting..."); + + println!("Disconnected!"); + return (); } - Err(_) => { - // println!("Perte de connexion avec le serveur"); - write!(io, "Perte de connexion avec le serveur\n").unwrap(); - io.flush().unwrap(); - return; + line => { + send(BufWriter::new(&stream), line); } } - let reponse = String::from_utf8(buff.to_vec()).unwrap(); - write!(io, "{}", reponse).unwrap(); - io.flush().unwrap(); - // println!("From server: {}", reponse); } } -fn exchange_with_server( - mut stream: TcpStream -) { - let stdout = stdout(); - let mut io = stdout.lock(); - let _buff = &mut [0; 1024]; +fn exchange_with_server(stream: TcpStream) { + let server = stream.peer_addr().unwrap(); + println!("Connected to {}", server); + // Buffered reading and writing + let mut reader = BufReader::new(&stream); + let mut writer = BufWriter::new(&stream); - let stream_cpy = stream.try_clone().unwrap(); - spawn(move || { - // let stream_cpy = stream.try_clone().unwrap(); - read_from_server(stream_cpy); + println!("Enter `/quit` when you want to leave"); + + 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 + }) + } + + thread::spawn(move || { + write_to_server(stream.try_clone().unwrap()); }); - println!("Enter `quit` or `exit` when you want to leave"); + match(|| { + loop { + let input = receive!(); + println!("{}", input); + } - 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(buff) { - // 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); - } + })() { + Ok(_) => { + println!("Left?"); + } + Err(e) => { + println!("Disappeared? {}", e); } } } diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index 7e358f4..175d294 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -1,92 +1,169 @@ -extern crate bufstream; -use std::io::{BufRead, Write}; +use std::io::*; use std::net::{SocketAddr, TcpListener, TcpStream}; -use std::str::FromStr; -use std::sync::{mpsc, Arc, RwLock}; -use std::sync::mpsc::{Receiver, Sender}; -use std::thread::spawn; -use self::bufstream::BufStream; +use std::thread; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::collections::HashMap; -fn handle_connection( - stream: &mut BufStream, - chan: Sender, - arc: Arc>>, -) { - stream.write(b"Welcome this server!\n").unwrap(); - stream - .write(b"Please input your username (max. 20chars): ") - .unwrap(); - stream.flush().unwrap(); +// Map for all connected clients containing their name and stream +type UserMapValue = (String, TcpStream); +type UserMap = HashMap; - let mut name = String::new(); - stream.read_line(&mut name).unwrap(); - let name = name.trim_right(); - stream - .write_fmt(format_args!("Hello, {}!\n", name)) - .unwrap(); - stream.flush().unwrap(); - - let mut pos = 0; - loop { - { - let lines = arc.read().unwrap(); - for i in pos..lines.len() { - stream.write_fmt(format_args!("{}", lines[i])).unwrap(); - pos = lines.len(); +fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard) { + for (other_client, entry) in (*lock).iter() { + if other_client != not_to { + let other_name = &entry.0; + let other_stream = &entry.1; + match (|| -> Result<()> { + let mut writer = BufWriter::new(other_stream); + try!(writeln!(writer, "{}", msg)); + try!(writer.flush()); + return Ok(()); + })() + { + Ok(_) => {} + Err(e) => { + println!( + "Client {} <{}> disappeared during message distribution: {}", + other_client, other_name, e + ); + } } } - stream.write(b" > ").unwrap(); - stream.flush().unwrap(); + } +} - let mut reads = String::new(); - stream.read_line(&mut reads).unwrap(); - if reads.trim().len() != 0 { - chan.send(format!("[{}] said: {}", name, reads)).unwrap(); +fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard) { + (*lock).remove(&client); + distribute_message(&format!("{} left", name), client, lock); +} + +fn handle_client(stream: TcpStream, clients: Arc>) { + // Get client IP and port + let client = stream.peer_addr().unwrap(); + println!("New connection from {}", 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 + }) + } + + // Initialization: Ask user for his name + let name = match (|| { + send!("Welcome!"); + send!("Please enter your name:"); + let name = receive!(); + println!("Client {} identified as {}", client, name); + send!("DEBUG: You can now type messages. Leave this chat with the request `BYE`."); + Ok(name) + })() + { + Ok(name) => name, + Err(e) => { + println!("Client {} disappeared during initialization: {}", client, e); + 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!("{} joined", name), &client, &mut lock); + } + + // Chat loop: Receive messages from users + match (|| { + loop { + let input = receive!(); + if input == "BYE" { + send!("Bye!"); + return Ok(()); + } + + // Distribute message + println!("{} <{}>: {}", client, name, input); + { + let mut lock = clients.lock().unwrap(); + distribute_message(&format!("<{}>: {}", name, input), &client, &mut lock); + } + } + })() + { + Ok(_) => { + println!("Client {} <{}> left", client, name); + } + Err(e) => { + println!( + "Client {} <{}> disappeared during chat: {}", + 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) { - // Ouverture de la connexion sur socket - let addr = SocketAddr::from_str(&addr).unwrap(); - // Ajout d’un listener Tcp sur le socket - let listener = TcpListener::bind(addr).unwrap(); + // Manage UserMap in a mutex + let clients = Arc::new(Mutex::new(HashMap::new())); + let serv_addr = addr.clone(); - // création des receveurs et envoyeurs de strings asynchrones - let (sender, receiver): (Sender, Receiver) = mpsc::channel(); - let arc: Arc>> = Arc::new(RwLock::new(Vec::new())); - let arc_w = arc.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), + }; - // boucle infinie en parallèle pour recevoir des messages - spawn(move || { - loop { - // lit le message depuis le receveur - let msg = receiver.recv().unwrap(); - print!("DEBUG: message {}", msg); - { - let mut arc_w = arc_w.write().unwrap(); - arc_w.push(msg); - } - } - }); + println!("Successfully started the server on {}", serv_addr); - // Réception des clients for stream in listener.incoming() { match stream { - Err(e) => println!("Erreur écoute : {}", e), - Ok(mut stream) => { - println!( - "Nouvelle connexion de {} vers {}", - stream.peer_addr().unwrap(), - stream.local_addr().unwrap() - ); - let sender = sender.clone(); - let arc = arc.clone(); - spawn(move || { - let mut stream = BufStream::new(stream); - handle_connection(&mut stream, sender, arc); + 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); } From d085789fab2af19d932eb4a2b4aa1541437f7d79 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 18 Mar 2018 22:03:50 +0100 Subject: [PATCH 04/34] webhook test for Discord (also still errors for macros) --- Lucien/Rust/src/client.rs | 46 +++++++++++++++++---------------------- Lucien/Rust/src/main.rs | 2 ++ 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 207865a..0a01301 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -1,20 +1,10 @@ +use std; use std::io::*; use std::net::TcpStream; use std::thread; static leave_msg: &str = "BYE"; -// macro_rules! send { -// ($line:expr) => ({ -// try!(writeln!(writer, "{}", $line)); -// try!(writer.flush()); -// }) -// } - -fn send(writer: BufWriter<&TcpStream>,text: &str) { - -} - fn get_entry() -> String { let mut buf = String::new(); @@ -23,19 +13,25 @@ fn get_entry() -> String { } fn write_to_server(stream: TcpStream) { - let mut writer = BufWriter::new(&stream); + macro_rules! send { + ($line:expr) => ({ + try!(writeln!(writer, "{}", $line)); + try!(writer.flush()); + }) + } + loop { match &*get_entry() { "/quit" => { println!("Disconnecting..."); - + send!("BYE"); println!("Disconnected!"); return (); } line => { - send(BufWriter::new(&stream), line); + send!(line); } } } @@ -56,14 +52,14 @@ fn exchange_with_server(stream: TcpStream) { match reader.read_line(&mut line) { Ok(len) => { if len == 0 { - // Reader is at EOF. - return Err(Error::new(ErrorKind::Other, "unexpected EOF")); + // 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>; + // return Err(Error::new(ErrorKind::Other, "unexpected EOF")); } line.pop(); } - Err(e) => { - return Err(e); - } + Err(e) => { return Err(e); } }; line }) @@ -73,13 +69,11 @@ fn exchange_with_server(stream: TcpStream) { write_to_server(stream.try_clone().unwrap()); }); - match(|| { - loop { - let input = receive!(); - println!("{}", input); - } - - })() { + match (|| loop { + let input = receive!(); + println!("{}", input); + })() + { Ok(_) => { println!("Left?"); } diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs index 7aa08a4..0b90c76 100644 --- a/Lucien/Rust/src/main.rs +++ b/Lucien/Rust/src/main.rs @@ -1,3 +1,5 @@ +#![feature(type_ascription)] + use std::env; pub mod client; From b9a13e29dd3d5870a92260d2ce61b241ff9802c6 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 12:12:35 +0100 Subject: [PATCH 05/34] Client finally sends and displays properly messages --- Lucien/Rust/src/client.rs | 53 +++++++++------------------------------ 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 0a01301..6da8106 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -3,7 +3,7 @@ use std::io::*; use std::net::TcpStream; use std::thread; -static leave_msg: &str = "BYE"; +// static leave_msg: &str = "BYE"; fn get_entry() -> String { let mut buf = String::new(); @@ -15,23 +15,20 @@ fn get_entry() -> String { fn write_to_server(stream: TcpStream) { let mut writer = BufWriter::new(&stream); - macro_rules! send { - ($line:expr) => ({ - try!(writeln!(writer, "{}", $line)); - try!(writer.flush()); - }) - } - loop { match &*get_entry() { "/quit" => { println!("Disconnecting..."); - send!("BYE"); + // send!("BYE"); + writeln!(writer, "BYE").unwrap(); + writer.flush().unwrap(); println!("Disconnected!"); return (); } line => { - send!(line); + // send!(line); + writeln!(writer, "{}", line).unwrap(); + writer.flush().unwrap(); } } } @@ -41,8 +38,9 @@ fn exchange_with_server(stream: TcpStream) { let server = stream.peer_addr().unwrap(); println!("Connected to {}", server); // Buffered reading and writing - let mut reader = BufReader::new(&stream); - let mut writer = BufWriter::new(&stream); + let stream_cpy = stream.try_clone().unwrap(); + let mut reader = BufReader::new(&stream_cpy); + // let mut writer = BufWriter::new(&stream); println!("Enter `/quit` when you want to leave"); @@ -77,39 +75,12 @@ fn exchange_with_server(stream: TcpStream) { Ok(_) => { println!("Left?"); } - Err(e) => { - println!("Disappeared? {}", e); + Err(_) => { + println!(">>> Successfully left the room <<<"); } } } -// fn exchange_with_server(stream: TcpStream) { -// let (chan, recv): (Sender, Receiver) = mpsc::channel(); -// // let buf = &mut [0; 1024]; -// spawn(move || { -// loop { -// let msg = recv.recv().unwrap(); -// println!("{}", msg); -// } -// }); -// println!("Enter `quit` or `exit` when you want to leave"); -// loop { -// match &*get_entry() { -// "quit" => { -// println!("bye!"); -// return; -// } -// "exit" => { -// println!("bye!"); -// return; -// } -// line => { -// chan.send(format!("{}", line)).unwrap(); -// } -// } -// } -// } - pub fn client(server_address: String) { println!("Tentative de connexion a serveur..."); match TcpStream::connect(server_address) { From 005d5d3b19738728023f639c5d1837757bc64c4e Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 12:55:10 +0100 Subject: [PATCH 06/34] minor changes --- Lucien/Rust/src/client.rs | 2 -- Lucien/Rust/src/main.rs | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 6da8106..694ba30 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -19,14 +19,12 @@ fn write_to_server(stream: TcpStream) { match &*get_entry() { "/quit" => { println!("Disconnecting..."); - // send!("BYE"); writeln!(writer, "BYE").unwrap(); writer.flush().unwrap(); println!("Disconnected!"); return (); } line => { - // send!(line); writeln!(writer, "{}", line).unwrap(); writer.flush().unwrap(); } diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs index 0b90c76..df85bcb 100644 --- a/Lucien/Rust/src/main.rs +++ b/Lucien/Rust/src/main.rs @@ -21,7 +21,11 @@ fn main() { // Client opened // /////////////////////////////////////////////////////////////////////// println!("Client connecting on server {}:{}", args[1], args[2]); - let mut serv = args[1].clone(); + 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); From e35e75ea227287a5c08c234b9afcf6b98c75fb79 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 13:09:11 +0100 Subject: [PATCH 07/34] removed unused dependency --- Lucien/Rust/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Lucien/Rust/Cargo.toml b/Lucien/Rust/Cargo.toml index 7e36bbf..6171117 100644 --- a/Lucien/Rust/Cargo.toml +++ b/Lucien/Rust/Cargo.toml @@ -4,4 +4,3 @@ version = "0.2.0" authors = ["Lucien Cartier-Tilet "] [dependencies] -bufstream = "0.1" From 5de131064f175629969ec8c3488fea08fef299c6 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 12:27:58 +0000 Subject: [PATCH 08/34] fixed server adress --- Lucien/Rust/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs index df85bcb..c530503 100644 --- a/Lucien/Rust/src/main.rs +++ b/Lucien/Rust/src/main.rs @@ -13,7 +13,7 @@ fn main() { /////////////////////////////////////////////////////////////////////// println!("Opening server on port {}", args[1]); // serveur(args[1].clone()); - let mut serv = String::from("127.0.0.1:"); + let mut serv = String::from("0.0.0.0:"); serv.push_str(&args[1]); server::serveur(serv); } else if args.len() == 3 { From 50e4b6a38085571a1d5b5dbdae37b3222e3f6d55 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 15:51:34 +0100 Subject: [PATCH 09/34] protocol further implemented --- Lucien/Rust/src/client.rs | 56 +++++++++++++++++++-- Lucien/Rust/src/server.rs | 101 ++++++++++++++++++++++++++++++++------ 2 files changed, 138 insertions(+), 19 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 694ba30..ac866d2 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -15,8 +15,12 @@ fn get_entry() -> String { fn write_to_server(stream: TcpStream) { let mut writer = BufWriter::new(&stream); + // entrée du nom d'utilisateur loop { match &*get_entry() { + "" => { + continue; + } "/quit" => { println!("Disconnecting..."); writeln!(writer, "BYE").unwrap(); @@ -25,20 +29,50 @@ fn write_to_server(stream: TcpStream) { return (); } line => { + let line_str : String = String::from(line); + // let spliced: Vec<&str> = line_str.split(" ").collect(); + let spliced: Vec<&str> = line_str.split_whitespace().collect(); + if spliced.len() > 1 { + println!("Cannot use whitespace in username."); + continue; + } writeln!(writer, "{}", line).unwrap(); writer.flush().unwrap(); } } + break; + } + + loop { + match &*get_entry() { + "" => { + ; + } + "/quit" => { + println!("Disconnecting..."); + writeln!(writer, "BYE").unwrap(); + writer.flush().unwrap(); + println!("Disconnected!"); + return (); + } + "/clients" => { + writeln!(writer, "REQ CLIENTS").unwrap(); + writer.flush().unwrap(); + } + line => { + writeln!(writer, "MSG {}", line).unwrap(); + writer.flush().unwrap(); + } + } } } fn exchange_with_server(stream: TcpStream) { let server = stream.peer_addr().unwrap(); println!("Connected to {}", server); - // Buffered reading and writing + let stream_cpy = stream.try_clone().unwrap(); let mut reader = BufReader::new(&stream_cpy); - // let mut writer = BufWriter::new(&stream); println!("Enter `/quit` when you want to leave"); @@ -51,7 +85,6 @@ fn exchange_with_server(stream: TcpStream) { // 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>; - // return Err(Error::new(ErrorKind::Other, "unexpected EOF")); } line.pop(); } @@ -66,8 +99,21 @@ fn exchange_with_server(stream: TcpStream) { }); match (|| loop { - let input = receive!(); - println!("{}", input); + let input: String = String::from(receive!()); + let spliced_input: Vec<&str> = input.split(" ").collect(); + // if spliced_input[0] == "FROM" { + // println!("<{}>: {}", spliced_input[1], spliced_input[3]); + // continue; + // } + match spliced_input[0] { + "FROM" => { + println!("<{}>: {}", spliced_input[1], spliced_input[3]); + } + _ => { + println!("{}", input); + } + } + // println!("{}", input); })() { Ok(_) => { diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index 175d294..8053f2e 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -4,18 +4,61 @@ use std::thread; use std::sync::{Arc, Mutex, MutexGuard}; use std::collections::HashMap; +/////////////////////////////////////////////////////////////////////////////// +// Evolution implementation protocole // +/////////////////////////////////////////////////////////////////////////////// + +/* + +1.1 [ ] +1.2 [ ] +1.3 [ ] +1.4 [ ] +1.5 [ ] +1.6 [ ] +1.7 [X] +1.8 [ ] +1.9 [ ] +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] + +*/ + +/////////////////////////////////////////////////////////////////////////////// +// code // +/////////////////////////////////////////////////////////////////////////////// + // Map for all connected clients containing their name and stream type UserMapValue = (String, TcpStream); type UserMap = HashMap; fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard) { + 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() { if other_client != not_to { let other_name = &entry.0; let other_stream = &entry.1; match (|| -> Result<()> { let mut writer = BufWriter::new(other_stream); - try!(writeln!(writer, "{}", msg)); + // 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(()); })() @@ -32,9 +75,28 @@ fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard) { + let mut clients = String::new(); + for (client, entry) in (*lock).iter() { + if client != to { + clients.push_str(&format!("{} ", &entry.0)); + } + } + println!("{}", clients); + for (client, entry) in (*lock).iter() { + if client == to { + let stream = &entry.1; + let mut writer = BufWriter::new(stream); + writeln!(writer, "{}", clients).unwrap(); + writer.flush().unwrap(); + return; + } + } +} + fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard) { (*lock).remove(&client); - distribute_message(&format!("{} left", name), client, lock); + distribute_message(&format!("LOGOUT {}", name), client, lock); } fn handle_client(stream: TcpStream, clients: Arc>) { @@ -97,23 +159,34 @@ fn handle_client(stream: TcpStream, clients: Arc>) { { let mut lock = clients.lock().unwrap(); (*lock).insert(client, (name.clone(), stream.try_clone().unwrap())); - distribute_message(&format!("{} joined", name), &client, &mut lock); + distribute_message(&format!("JOIN {}", name), &client, &mut lock); } + writeln!(writer, "WELCOME").unwrap(); + writer.flush().unwrap(); + // Chat loop: Receive messages from users - match (|| { - loop { - let input = receive!(); - if input == "BYE" { - send!("Bye!"); + match (|| loop { + match receive!().as_str() { + "BYE" => { + send!("BYE"); return Ok(()); } - - // Distribute message - println!("{} <{}>: {}", client, name, input); - { - let mut lock = clients.lock().unwrap(); - distribute_message(&format!("<{}>: {}", name, input), &client, &mut lock); + "PING" => { + send!("PONG"); + } + "REQ CLIENTS" => { + { + let mut lock = clients.lock().unwrap(); + send_clients_name(&client, &mut lock); + } + } + input => { + println!("{} <{}>: {}", client, name, input); + { + let mut lock = clients.lock().unwrap(); + distribute_message(&format!("{}", input), &client, &mut lock); + } } } })() From 60bedb7ff6a1313821517ae12cb30fb8714959ab Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 20:38:20 +0100 Subject: [PATCH 10/34] Further improvements for the server and client --- Lucien/Rust/Cargo.toml | 2 + Lucien/Rust/src/client.rs | 104 +++++++++++++++++++++++++++----------- Lucien/Rust/src/server.rs | 90 +++++++++++++++++++-------------- 3 files changed, 127 insertions(+), 69 deletions(-) diff --git a/Lucien/Rust/Cargo.toml b/Lucien/Rust/Cargo.toml index 6171117..cf2ba81 100644 --- a/Lucien/Rust/Cargo.toml +++ b/Lucien/Rust/Cargo.toml @@ -4,3 +4,5 @@ version = "0.2.0" authors = ["Lucien Cartier-Tilet "] [dependencies] +colored = "1.6" +chrono = "0.4" diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index ac866d2..647a51a 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -1,7 +1,11 @@ +extern crate chrono; +extern crate colored; use std; use std::io::*; use std::net::TcpStream; use std::thread; +use self::colored::*; +use self::chrono::Local; // static leave_msg: &str = "BYE"; @@ -12,12 +16,11 @@ fn get_entry() -> String { buf.replace("\n", "").replace("\r", "") } -fn write_to_server(stream: TcpStream) { - let mut writer = BufWriter::new(&stream); - - // entrée du nom d'utilisateur +fn get_name(writer: &mut BufWriter<&TcpStream>) { loop { - match &*get_entry() { + let mut line = &*get_entry(); + line = line.trim(); + match line { "" => { continue; } @@ -25,11 +28,10 @@ fn write_to_server(stream: TcpStream) { println!("Disconnecting..."); writeln!(writer, "BYE").unwrap(); writer.flush().unwrap(); - println!("Disconnected!"); return (); } line => { - let line_str : String = String::from(line); + let line_str: String = String::from(line); // let spliced: Vec<&str> = line_str.split(" ").collect(); let spliced: Vec<&str> = line_str.split_whitespace().collect(); if spliced.len() > 1 { @@ -40,19 +42,24 @@ fn write_to_server(stream: TcpStream) { writer.flush().unwrap(); } } - break; + return; } +} + +fn write_to_server(stream: TcpStream) { + let mut writer = BufWriter::new(&stream); + + // entrée du nom d'utilisateur + get_name(&mut writer); loop { - match &*get_entry() { - "" => { - ; - } + let line = &*get_entry(); + line.trim(); + match line { + "" => {} "/quit" => { - println!("Disconnecting..."); writeln!(writer, "BYE").unwrap(); writer.flush().unwrap(); - println!("Disconnected!"); return (); } "/clients" => { @@ -74,17 +81,18 @@ fn exchange_with_server(stream: TcpStream) { let stream_cpy = stream.try_clone().unwrap(); let mut reader = BufReader::new(&stream_cpy); - println!("Enter `/quit` when you want to leave"); - 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>; + // 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(); } @@ -101,13 +109,50 @@ fn exchange_with_server(stream: TcpStream) { match (|| loop { let input: String = String::from(receive!()); let spliced_input: Vec<&str> = input.split(" ").collect(); - // if spliced_input[0] == "FROM" { - // println!("<{}>: {}", spliced_input[1], spliced_input[3]); - // continue; - // } match spliced_input[0] { + "WELCOME" => { + println!("{}", "Successfully connected to the server.".green()); + println!("Type /clients to get the list of users connected"); + println!("Type /quit to disconnect and quit"); + } "FROM" => { - println!("<{}>: {}", spliced_input[1], spliced_input[3]); + let date = Local::now(); + 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().blue(), + spliced_input[1].red(), + msg + ); + } + "BYE" => { + return Ok("Ok"); + } + "LIST" => { + println!("{}", ">>>> LIST OF CLIENTS CONNECTED <<<<".bold().yellow()); + for i in 2..spliced_input.len() { + println!("\t\t{}", spliced_input[i]); + } + } + "JOIN" => { + println!( + "{}{}{}", + "-------> ".green(), + spliced_input[1], + " has joined".green() + ); + } + "LOGOUT" => { + println!( + "{}{}{}", + "<------- ".red(), + spliced_input[1], + " has left".red() + ); } _ => { println!("{}", input); @@ -117,23 +162,22 @@ fn exchange_with_server(stream: TcpStream) { })() { Ok(_) => { - println!("Left?"); + println!("{}", ">>> Successfully left the room <<<".green()); } - Err(_) => { - println!(">>> Successfully left the room <<<"); + Err(e) => { + println!("{}: {}", "Error: Function terminated too early".red(), e); } } } pub fn client(server_address: String) { - println!("Tentative de connexion a serveur..."); + println!("Trying to connect to the server..."); 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); + println!("{} {}", "Connection to server failed:".red(), e); return; } } diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index 8053f2e..e46a8f9 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; 1.5 [ ] 1.6 [ ] 1.7 [X] -1.8 [ ] +1.8 [X] 1.9 [ ] 2.1 [X] 2.2 [X] @@ -26,19 +26,28 @@ use std::collections::HashMap; 4.1.1 [X] 4.1.2 [X] 4.2.1 [X] -4.2.2 [X] +4.2.2 [-] */ /////////////////////////////////////////////////////////////////////////////// -// code // +// TYPES // /////////////////////////////////////////////////////////////////////////////// // Map for all connected clients containing their name and stream type UserMapValue = (String, TcpStream); type UserMap = HashMap; -fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard) { +/////////////////////////////////////////////////////////////////////////////// +// CODE // +/////////////////////////////////////////////////////////////////////////////// + +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 { @@ -47,29 +56,30 @@ fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard 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: {}", - other_client, other_name, e - ); - } + 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: {}", + other_client, other_name, e + ); } } } @@ -78,16 +88,21 @@ fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard) { let mut clients = String::new(); for (client, entry) in (*lock).iter() { - if client != to { - clients.push_str(&format!("{} ", &entry.0)); - } + &entry.0.trim_left(); + &entry.0.trim_right(); + clients.push_str(&format!( + "{}{} ", + if client == to { "(You)" } else { "" }, + &entry.0 + )); } + clients.trim_left(); println!("{}", clients); for (client, entry) in (*lock).iter() { if client == to { let stream = &entry.1; let mut writer = BufWriter::new(stream); - writeln!(writer, "{}", clients).unwrap(); + writeln!(writer, "LIST CLIENTS {}", clients).unwrap(); writer.flush().unwrap(); return; } @@ -96,7 +111,7 @@ fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard) { fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard) { (*lock).remove(&client); - distribute_message(&format!("LOGOUT {}", name), client, lock); + distribute_message(&format!("LOGOUT {}", name), client, lock, true); } fn handle_client(stream: TcpStream, clients: Arc>) { @@ -144,7 +159,6 @@ fn handle_client(stream: TcpStream, clients: Arc>) { send!("Please enter your name:"); let name = receive!(); println!("Client {} identified as {}", client, name); - send!("DEBUG: You can now type messages. Leave this chat with the request `BYE`."); Ok(name) })() { @@ -159,7 +173,7 @@ fn handle_client(stream: TcpStream, clients: Arc>) { { let mut lock = clients.lock().unwrap(); (*lock).insert(client, (name.clone(), stream.try_clone().unwrap())); - distribute_message(&format!("JOIN {}", name), &client, &mut lock); + distribute_message(&format!("JOIN {}", name), &client, &mut lock, false); } writeln!(writer, "WELCOME").unwrap(); @@ -176,16 +190,14 @@ fn handle_client(stream: TcpStream, clients: Arc>) { send!("PONG"); } "REQ CLIENTS" => { - { - let mut lock = clients.lock().unwrap(); - send_clients_name(&client, &mut lock); - } + let mut lock = clients.lock().unwrap(); + send_clients_name(&client, &mut lock); } input => { println!("{} <{}>: {}", client, name, input); { let mut lock = clients.lock().unwrap(); - distribute_message(&format!("{}", input), &client, &mut lock); + distribute_message(&format!("{}", input), &client, &mut lock, true); } } } From 76c8ece29ae622d5fa67052ae86befbc559e2e77 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 21:48:40 +0100 Subject: [PATCH 11/34] minor text changes for client --- Lucien/Rust/src/client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 647a51a..3eea79c 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -124,7 +124,7 @@ fn exchange_with_server(stream: TcpStream) { } println!( "{} <{}>:{}", - date.format("%H:%M:%S").to_string().blue(), + date.format("[%H:%M:%S]").to_string().blue(), spliced_input[1].red(), msg ); @@ -162,7 +162,7 @@ fn exchange_with_server(stream: TcpStream) { })() { Ok(_) => { - println!("{}", ">>> Successfully left the room <<<".green()); + println!("{}", ">>> Logout successful <<<".green()); } Err(e) => { println!("{}: {}", "Error: Function terminated too early".red(), e); From ff88e2133d001ea9b395fb26a06bed69bd5f2f64 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 23:23:14 +0100 Subject: [PATCH 12/34] Fixed request 4.2.2 in server --- Lucien/Rust/src/server.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index e46a8f9..449da89 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -88,16 +88,13 @@ fn distribute_message( fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard) { let mut clients = String::new(); for (client, entry) in (*lock).iter() { - &entry.0.trim_left(); - &entry.0.trim_right(); clients.push_str(&format!( "{}{} ", - if client == to { "(You)" } else { "" }, - &entry.0 + &entry.0.trim(), + if client == to { "(you)" } else { "" } )); } - clients.trim_left(); - println!("{}", clients); + let clients = clients.trim(); for (client, entry) in (*lock).iter() { if client == to { let stream = &entry.1; From 92be982856d5d4fdc3b327eae8604670867882e2 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 21 Mar 2018 23:23:54 +0100 Subject: [PATCH 13/34] Enhanced client colors and display --- Lucien/Rust/src/client.rs | 79 ++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 3eea79c..6c193d0 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -6,12 +6,18 @@ use std::net::TcpStream; use std::thread; use self::colored::*; use self::chrono::Local; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; -// static leave_msg: &str = "BYE"; +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_entry() -> String { let mut buf = String::new(); - stdin().read_line(&mut buf).unwrap(); buf.replace("\n", "").replace("\r", "") } @@ -78,6 +84,18 @@ 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); @@ -111,22 +129,39 @@ fn exchange_with_server(stream: TcpStream) { let spliced_input: Vec<&str> = input.split(" ").collect(); match spliced_input[0] { "WELCOME" => { - println!("{}", "Successfully connected to the server.".green()); + println!("{}", ">>> Login Successful <<<".green()); println!("Type /clients to get the list of users connected"); println!("Type /quit to disconnect and quit"); } "FROM" => { let date = Local::now(); + let name = String::from(spliced_input[1]); + let mut msg = String::new(); for i in 3..spliced_input.len() { msg.push_str(" "); msg.push_str(spliced_input[i]); } + + // Hashing name for color + let mut s = DefaultHasher::new(); + name.hash(&mut s); + let name_hash: usize = (s.finish() as usize) % 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('>'); + println!( - "{} <{}>:{}", - date.format("[%H:%M:%S]").to_string().blue(), - spliced_input[1].red(), - msg + "{} {}:{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + name.color(COLORS[name_hash]), + msg.yellow().dimmed() ); } "BYE" => { @@ -139,20 +174,30 @@ fn exchange_with_server(stream: TcpStream) { } } "JOIN" => { + let date = Local::now(); + let name_hash: usize = + hash_name(spliced_input[1].clone()) % COLORS.len(); + println!( - "{}{}{}", - "-------> ".green(), - spliced_input[1], + "{}{}{}{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + " ------> ".green(), + spliced_input[1].color(COLORS[name_hash]), " has joined".green() - ); + ) } "LOGOUT" => { + let date = Local::now(); + let name_hash: usize = + hash_name(spliced_input[1].clone()) % COLORS.len(); + println!( - "{}{}{}", - "<------- ".red(), - spliced_input[1], + "{}{}{}{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + " <------ ".red(), + spliced_input[1].color(COLORS[name_hash]), " has left".red() - ); + ) } _ => { println!("{}", input); @@ -164,8 +209,8 @@ fn exchange_with_server(stream: TcpStream) { Ok(_) => { println!("{}", ">>> Logout successful <<<".green()); } - Err(e) => { - println!("{}: {}", "Error: Function terminated too early".red(), e); + Err(_) => { + println!("{}", "Error: Connection with server lost".red()); } } } From cea07a8e19698efb8cb03806af58643ba3117d0a Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Thu, 22 Mar 2018 00:06:15 +0100 Subject: [PATCH 14/34] Limited username length to 20 characters --- Lucien/Rust/src/client.rs | 18 +++++++++++------- Lucien/Rust/src/server.rs | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 6c193d0..154f81a 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -4,10 +4,13 @@ use std; use std::io::*; use std::net::TcpStream; use std::thread; -use self::colored::*; -use self::chrono::Local; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; +use self::colored::*; +use self::chrono::Local; + +// TODO: Limit usernames to ascii +// TODO: implement requests 1.x from protocol fn hash_name(name: &str) -> usize { let mut s = DefaultHasher::new(); @@ -26,6 +29,10 @@ fn get_name(writer: &mut BufWriter<&TcpStream>) { loop { let mut line = &*get_entry(); line = line.trim(); + if line.len() > 20 { + println!("Nickname too long, it must be at most 20 characters long"); + continue; + } match line { "" => { continue; @@ -38,7 +45,6 @@ fn get_name(writer: &mut BufWriter<&TcpStream>) { } line => { let line_str: String = String::from(line); - // let spliced: Vec<&str> = line_str.split(" ").collect(); let spliced: Vec<&str> = line_str.split_whitespace().collect(); if spliced.len() > 1 { println!("Cannot use whitespace in username."); @@ -175,8 +181,7 @@ fn exchange_with_server(stream: TcpStream) { } "JOIN" => { let date = Local::now(); - let name_hash: usize = - hash_name(spliced_input[1].clone()) % COLORS.len(); + let name_hash: usize = hash_name(spliced_input[1].clone()) % COLORS.len(); println!( "{}{}{}{}", @@ -188,8 +193,7 @@ fn exchange_with_server(stream: TcpStream) { } "LOGOUT" => { let date = Local::now(); - let name_hash: usize = - hash_name(spliced_input[1].clone()) % COLORS.len(); + let name_hash: usize = hash_name(spliced_input[1].clone()) % COLORS.len(); println!( "{}{}{}{}", diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index 449da89..cd452b9 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -4,6 +4,8 @@ use std::thread; use std::sync::{Arc, Mutex, MutexGuard}; use std::collections::HashMap; +// TODO: implement requests 1.x from protocol + /////////////////////////////////////////////////////////////////////////////// // Evolution implementation protocole // /////////////////////////////////////////////////////////////////////////////// From de2523b083f18c2eb6b0014ad1f212e526b37f9b Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Thu, 22 Mar 2018 00:10:32 +0100 Subject: [PATCH 15/34] Set limit of messages at 2000 characters --- Lucien/Rust/src/client.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 154f81a..5f80b60 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -79,6 +79,14 @@ fn write_to_server(stream: TcpStream) { 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(); } From 66a6bc2bc5bdd12ce289fac197b7841b512cd0c9 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Thu, 22 Mar 2018 11:36:06 +0100 Subject: [PATCH 16/34] Improved message display in client --- Lucien/Rust/Cargo.toml | 1 + Lucien/Rust/src/client.rs | 81 +++++++++++++++++++++++++++++++-------- Lucien/Rust/src/main.rs | 2 +- Lucien/Rust/src/server.rs | 1 + 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/Lucien/Rust/Cargo.toml b/Lucien/Rust/Cargo.toml index cf2ba81..b2bfffe 100644 --- a/Lucien/Rust/Cargo.toml +++ b/Lucien/Rust/Cargo.toml @@ -6,3 +6,4 @@ authors = ["Lucien Cartier-Tilet "] [dependencies] colored = "1.6" chrono = "0.4" +term_size = "0.3" diff --git a/Lucien/Rust/src/client.rs b/Lucien/Rust/src/client.rs index 5f80b60..8954184 100644 --- a/Lucien/Rust/src/client.rs +++ b/Lucien/Rust/src/client.rs @@ -1,5 +1,6 @@ extern crate chrono; extern crate colored; +extern crate term_size; use std; use std::io::*; use std::net::TcpStream; @@ -10,7 +11,8 @@ use self::colored::*; use self::chrono::Local; // TODO: Limit usernames to ascii -// TODO: implement requests 1.x from protocol +// TODO: Implement requests 1.x from protocol +// TODO: forbid usernames already in use fn hash_name(name: &str) -> usize { let mut s = DefaultHasher::new(); @@ -82,8 +84,7 @@ fn write_to_server(stream: TcpStream) { if line.len() > 2000 { println!( "{}", - "Cannot send a message longer than 2000 characters" - .bright_red() + "Cannot send a message longer than 2000 characters".bright_red() ); continue; } @@ -150,12 +151,7 @@ fn exchange_with_server(stream: TcpStream) { "FROM" => { let date = Local::now(); let name = String::from(spliced_input[1]); - - let mut msg = String::new(); - for i in 3..spliced_input.len() { - msg.push_str(" "); - msg.push_str(spliced_input[i]); - } + let mut first_line = true; // Hashing name for color let mut s = DefaultHasher::new(); @@ -171,12 +167,63 @@ fn exchange_with_server(stream: TcpStream) { name.push_str(spliced_input[1]); name.push('>'); - println!( - "{} {}:{}", - date.format("[%H:%M:%S]").to_string().dimmed(), - name.color(COLORS[name_hash]), - msg.yellow().dimmed() - ); + if let Some((w, _)) = term_size::dimensions() { + let mut msg = String::new(); + let w = w - 34; + + for mut i in 3..spliced_input.len() { + if w > msg.len() + spliced_input[i].len() + 1 { + msg.push(' '); + msg.push_str(spliced_input[i]); + } else { + if first_line == true { + println!( + "{} {}:{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + name.color(COLORS[name_hash]), + msg.yellow().dimmed() + ); + first_line = false; + } else { + println!( + "{}{}", + " |".green(), + msg.yellow().dimmed() + ); + } + msg = String::new(); + #[allow(unused_assignments)] + i = i - 1; + } + } + + if first_line == true { + println!( + "{} {}:{}", + date.format("[%H:%M:%S]").to_string().dimmed(), + name.color(COLORS[name_hash]), + msg.yellow().dimmed() + ); + } else { + println!( + "{}{}", + " |".green(), + msg.yellow().dimmed() + ); + } + } 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"); @@ -194,7 +241,7 @@ fn exchange_with_server(stream: TcpStream) { println!( "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), - " ------> ".green(), + " ------> ".green(), spliced_input[1].color(COLORS[name_hash]), " has joined".green() ) @@ -206,7 +253,7 @@ fn exchange_with_server(stream: TcpStream) { println!( "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), - " <------ ".red(), + " <------ ".red(), spliced_input[1].color(COLORS[name_hash]), " has left".red() ) diff --git a/Lucien/Rust/src/main.rs b/Lucien/Rust/src/main.rs index c530503..0edb814 100644 --- a/Lucien/Rust/src/main.rs +++ b/Lucien/Rust/src/main.rs @@ -1,5 +1,5 @@ #![feature(type_ascription)] - +#![feature(stmt_expr_attributes)] use std::env; pub mod client; diff --git a/Lucien/Rust/src/server.rs b/Lucien/Rust/src/server.rs index cd452b9..3bcc982 100644 --- a/Lucien/Rust/src/server.rs +++ b/Lucien/Rust/src/server.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::collections::HashMap; // TODO: implement requests 1.x from protocol +// TODO: forbid usernames already in use /////////////////////////////////////////////////////////////////////////////// // Evolution implementation protocole // From 580e97f91b87525a5229689ee136a5c8193ff772 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Thu, 22 Mar 2018 14:12:58 +0100 Subject: [PATCH 17/34] Moved Rust source code --- .gitignore | 14 ++------------ {Lucien/Rust => Rust}/Cargo.toml | 0 {Lucien/Rust => Rust}/src/client.rs | 0 {Lucien/Rust => Rust}/src/main.rs | 0 {Lucien/Rust => Rust}/src/server.rs | 0 5 files changed, 2 insertions(+), 12 deletions(-) rename {Lucien/Rust => Rust}/Cargo.toml (100%) rename {Lucien/Rust => Rust}/src/client.rs (100%) rename {Lucien/Rust => Rust}/src/main.rs (100%) rename {Lucien/Rust => Rust}/src/server.rs (100%) diff --git a/.gitignore b/.gitignore index dbbd374..5126b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,15 +2,5 @@ /target **/*.rs.bk *.lock - -Lucien-Rust/target/ - -\.idea/ - -cmake-build-debug/ - -Lucien/Rust/target/ - -Lucien/Rust/Rust\.iml - -Lucien/server/ +*~ +Rust/target/ diff --git a/Lucien/Rust/Cargo.toml b/Rust/Cargo.toml similarity index 100% rename from Lucien/Rust/Cargo.toml rename to Rust/Cargo.toml diff --git a/Lucien/Rust/src/client.rs b/Rust/src/client.rs similarity index 100% rename from Lucien/Rust/src/client.rs rename to Rust/src/client.rs diff --git a/Lucien/Rust/src/main.rs b/Rust/src/main.rs similarity index 100% rename from Lucien/Rust/src/main.rs rename to Rust/src/main.rs diff --git a/Lucien/Rust/src/server.rs b/Rust/src/server.rs similarity index 100% rename from Lucien/Rust/src/server.rs rename to Rust/src/server.rs From 48e5c2825dab798926c33f7a3d4adafc12be6511 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Thu, 22 Mar 2018 16:10:16 +0100 Subject: [PATCH 18/34] Added timestamp to server --- Rust/Cargo.toml | 2 +- Rust/src/server.rs | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml index b2bfffe..c82a584 100644 --- a/Rust/Cargo.toml +++ b/Rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chat-reseau" -version = "0.2.0" +version = "0.3.0" authors = ["Lucien Cartier-Tilet "] [dependencies] diff --git a/Rust/src/server.rs b/Rust/src/server.rs index 3bcc982..1cdcb5e 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -1,8 +1,10 @@ +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: implement requests 1.x from protocol // TODO: forbid usernames already in use @@ -45,6 +47,11 @@ type UserMap = HashMap; // CODE // /////////////////////////////////////////////////////////////////////////////// +pub fn get_time() -> String { + let date = Local::now(); + date.format("[%H:%M:%S]").to_string() +} + fn distribute_message( msg: &str, not_to: &SocketAddr, @@ -80,8 +87,11 @@ fn distribute_message( Ok(_) => {} Err(e) => { println!( - "Client {} <{}> disappeared during message distribution: {}", - other_client, other_name, e + "{} Client {} <{}> disappeared during message distribution: {}", + get_time(), + other_client, + other_name, + e ); } } @@ -117,7 +127,7 @@ fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard>) { // Get client IP and port let client = stream.peer_addr().unwrap(); - println!("New connection from {}", client); + println!("{} New connection from {}", get_time(), client); // Buffered reading and writing let mut reader = BufReader::new(&stream); @@ -158,13 +168,18 @@ fn handle_client(stream: TcpStream, clients: Arc>) { send!("Welcome!"); send!("Please enter your name:"); let name = receive!(); - println!("Client {} identified as {}", client, name); + println!("{} Client {} identified as {}", get_time(), client, name); Ok(name) })() { Ok(name) => name, Err(e) => { - println!("Client {} disappeared during initialization: {}", client, e); + println!( + "{} Client {} disappeared during initialization: {}", + get_time(), + client, + e + ); return (); } }; @@ -194,7 +209,7 @@ fn handle_client(stream: TcpStream, clients: Arc>) { send_clients_name(&client, &mut lock); } input => { - println!("{} <{}>: {}", client, name, input); + println!("{} {} <{}>: {}", get_time(), client, name, input); { let mut lock = clients.lock().unwrap(); distribute_message(&format!("{}", input), &client, &mut lock, true); @@ -204,12 +219,15 @@ fn handle_client(stream: TcpStream, clients: Arc>) { })() { Ok(_) => { - println!("Client {} <{}> left", client, name); + println!("{} Client {} <{}> left", get_time(), client, name); } Err(e) => { println!( - "Client {} <{}> disappeared during chat: {}", - client, name, e + "{} Client {} <{}> disappeared during chat: {}", + get_time(), + client, + name, + e ); } } @@ -232,7 +250,11 @@ pub fn serveur(addr: String) { Err(e) => panic!("Could not read start TCP listener: {}", e), }; - println!("Successfully started the server on {}", serv_addr); + println!( + "{} Successfully started the server on {}", + get_time(), + serv_addr + ); for stream in listener.incoming() { match stream { From 4d51fef36503bd3ce2eb7178b10a1324ca663943 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 00:30:55 +0100 Subject: [PATCH 19/34] client broken (see TODO) but server pretty much complete --- Rust/src/client.rs | 26 ++++++- Rust/src/main.rs | 2 + Rust/src/server.rs | 189 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 181 insertions(+), 36 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 8954184..10b748d 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -10,9 +10,31 @@ use std::hash::{Hash, Hasher}; use self::colored::*; use self::chrono::Local; +/* + +0.1 [ ] +1.1 [ ] +1.2 [ ] +1.3 [ ] +1.4 [ ] +1.5 [ ] +1.6 [ ] +1.7 [X] +1.8 [X] +1.9 [ ] +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 [-] + + */ + +// TODO: Implement requests 1.x from protocol (connection currently is broken) // TODO: Limit usernames to ascii -// TODO: Implement requests 1.x from protocol -// TODO: forbid usernames already in use fn hash_name(name: &str) -> usize { let mut s = DefaultHasher::new(); diff --git a/Rust/src/main.rs b/Rust/src/main.rs index 0edb814..cb13cb9 100644 --- a/Rust/src/main.rs +++ b/Rust/src/main.rs @@ -2,6 +2,8 @@ #![feature(stmt_expr_attributes)] use std::env; +static PROTOCOL: &'static str = "0.1"; + pub mod client; pub mod server; diff --git a/Rust/src/server.rs b/Rust/src/server.rs index 1cdcb5e..a53d8a8 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -15,15 +15,16 @@ use self::chrono::Local; /* -1.1 [ ] -1.2 [ ] -1.3 [ ] -1.4 [ ] -1.5 [ ] -1.6 [ ] +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 [ ] +1.9 [X] 2.1 [X] 2.2 [X] 3.1 [ ] // pas utile avec Rust @@ -163,23 +164,106 @@ fn handle_client(stream: TcpStream, clients: Arc>) { }) } - // Initialization: Ask user for his name - let name = match (|| { - send!("Welcome!"); - send!("Please enter your name:"); - let name = receive!(); - println!("{} Client {} identified as {}", get_time(), client, name); - Ok(name) + // // Initialization: Ask user for his name + // let name = match (|| { + // send!("Welcome!"); + // send!("Please enter your name:"); + // let name = receive!(); + // println!("{} Client {} identified as {}", get_time(), client, name); + // Ok(name) + // })() + // { + // Ok(name) => name, + // Err(e) => { + // println!( + // "{} Client {} disappeared during initialization: {}", + // get_time(), + // client, + // e + // ); + // return (); + // } + // }; + + let name: String = match (|| loop { + match receive!() { + input => { + let spliced_input: Vec<&str> = input.split_whitespace().collect(); + if spliced_input.len() != 4 && spliced_input.len() != 5 + || spliced_input[0] != "PROT" + { + // send!("BAD REQ"); + return Err(Error::new(ErrorKind::Other, "BAD REQ")); + } + if spliced_input[1] != ::PROTOCOL { + // send!("BAD PROT"); + 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 used = false; + { + let lock = clients.lock().unwrap(); + for (_, entry) in (*lock).iter() { + if username == entry.0 { + used = true; + break; + } + } + } + if used == false { + send!("NAME OK"); + return Ok(username); + } else { + send!("NAME FAILURE"); + } + } + } + + loop { + send!("NAME REQ"); + match receive!() { + 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 used = false; + { + let lock = clients.lock().unwrap(); + for (_, entry) in (*lock).iter() { + if username == entry.0 { + used = true; + break; + } + } + } + if used == false { + send!("NAME OK"); + return Ok(username); + } else { + send!("NAME FAILURE"); + } + } + } + } + // return Ok(String::new()); + } + } })() { Ok(name) => name, Err(e) => { println!( - "{} Client {} disappeared during initialization: {}", - get_time(), - client, - e + "{time} Client {addr} encountered an error: {err}", + time = get_time(), + addr = client, + err = e ); + writeln!(writer, "{}", e).unwrap(); + writer.flush().unwrap(); return (); } }; @@ -194,26 +278,63 @@ fn handle_client(stream: TcpStream, clients: Arc>) { writeln!(writer, "WELCOME").unwrap(); writer.flush().unwrap(); - // Chat loop: Receive messages from users + // Chat loop: Receive messages from users once connected match (|| loop { match receive!().as_str() { - "BYE" => { - send!("BYE"); - return Ok(()); - } - "PING" => { - send!("PONG"); - } - "REQ CLIENTS" => { - let mut lock = clients.lock().unwrap(); - send_clients_name(&client, &mut lock); - } input => { - println!("{} {} <{}>: {}", get_time(), client, name, input); - { - let mut lock = clients.lock().unwrap(); - distribute_message(&format!("{}", input), &client, &mut lock, true); + println!( + "{time} {nick}@{addr}: {message}", + time = get_time(), + addr = client, + nick = name, + message = input + ); + + match input { + "BYE" => { + send!("BYE"); + return Ok(()); + } + + "PING" => 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} Sending: BAD REQ. Cause: {message}", + time = get_time(), + message = input + ); + send!("BAD REQ"); + } + } + } } + // { + // let mut lock = clients.lock().unwrap(); + // distribute_message(&format!("{}", input), &client, &mut lock, true); + // } } } })() From 1873df9ef04f721a2d93afed8d1c026681b21503 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 00:33:57 +0100 Subject: [PATCH 20/34] Forgot to increment version of crate --- Rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml index c82a584..b00fa20 100644 --- a/Rust/Cargo.toml +++ b/Rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chat-reseau" -version = "0.3.0" +version = "0.5.0" authors = ["Lucien Cartier-Tilet "] [dependencies] From 475724a3060ad1325639f4e0b42f466a14610c10 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 02:19:09 +0100 Subject: [PATCH 21/34] Verifies client's name does not have whitespace --- Rust/src/server.rs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/Rust/src/server.rs b/Rust/src/server.rs index a53d8a8..b78c021 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -164,27 +164,7 @@ fn handle_client(stream: TcpStream, clients: Arc>) { }) } - // // Initialization: Ask user for his name - // let name = match (|| { - // send!("Welcome!"); - // send!("Please enter your name:"); - // let name = receive!(); - // println!("{} Client {} identified as {}", get_time(), client, name); - // Ok(name) - // })() - // { - // Ok(name) => name, - // Err(e) => { - // println!( - // "{} Client {} disappeared during initialization: {}", - // get_time(), - // client, - // e - // ); - // return (); - // } - // }; - + // Get user's name let name: String = match (|| loop { match receive!() { input => { @@ -192,11 +172,9 @@ fn handle_client(stream: TcpStream, clients: Arc>) { if spliced_input.len() != 4 && spliced_input.len() != 5 || spliced_input[0] != "PROT" { - // send!("BAD REQ"); return Err(Error::new(ErrorKind::Other, "BAD REQ")); } if spliced_input[1] != ::PROTOCOL { - // send!("BAD PROT"); return Err(Error::new(ErrorKind::Other, "BAD PROT")); } if spliced_input.len() == 5 { @@ -218,6 +196,8 @@ fn handle_client(stream: TcpStream, clients: Arc>) { } else { send!("NAME FAILURE"); } + } else { + return Err(Error::new(ErrorKind::Other, "BAD REQ")); } } From ef4572c3a500a4a701172204b618cd10959df4b0 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 02:19:27 +0100 Subject: [PATCH 22/34] Apparently working client despite ugly code --- Rust/src/client.rs | 74 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 10b748d..2c8d2b7 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -49,45 +49,37 @@ fn get_entry() -> String { buf.replace("\n", "").replace("\r", "") } -fn get_name(writer: &mut BufWriter<&TcpStream>) { +fn get_name() -> String { loop { - let mut line = &*get_entry(); - line = line.trim(); - if line.len() > 20 { - println!("Nickname too long, it must be at most 20 characters long"); + 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; } - match line { + match name { "" => { continue; } - "/quit" => { - println!("Disconnecting..."); - writeln!(writer, "BYE").unwrap(); - writer.flush().unwrap(); - return (); - } - line => { - let line_str: String = String::from(line); - let spliced: Vec<&str> = line_str.split_whitespace().collect(); - if spliced.len() > 1 { - println!("Cannot use whitespace in username."); + _ => { + let spliced_name: Vec<&str> = name.split_whitespace().collect(); + if spliced_name.len() != 1 { + println!("{}", "Cannot use whitespace in name".red()); continue; } - writeln!(writer, "{}", line).unwrap(); - writer.flush().unwrap(); + return String::from(name); } } - return; } } fn write_to_server(stream: TcpStream) { let mut writer = BufWriter::new(&stream); - // entrée du nom d'utilisateur - get_name(&mut writer); - loop { let line = &*get_entry(); line.trim(); @@ -135,6 +127,7 @@ fn exchange_with_server(stream: TcpStream) { let stream_cpy = stream.try_clone().unwrap(); let mut reader = BufReader::new(&stream_cpy); + let mut writer = BufWriter::new(&stream_cpy); macro_rules! receive { () => ({ @@ -157,6 +150,41 @@ fn exchange_with_server(stream: TcpStream) { }) } + // 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(e) => { + println!("{}", ">>> Login successful".green()); + String::new() + } + }; + thread::spawn(move || { write_to_server(stream.try_clone().unwrap()); }); From f56706beedc13ea815b052de97bb1ed7a1d39283 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 02:21:24 +0100 Subject: [PATCH 23/34] forgot crate version --- Rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml index b00fa20..1a74bbb 100644 --- a/Rust/Cargo.toml +++ b/Rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chat-reseau" -version = "0.5.0" +version = "0.5.2" authors = ["Lucien Cartier-Tilet "] [dependencies] From 03664fe69e4587862bbd8b5707b1fdfd5d6b9083 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 02:23:57 +0100 Subject: [PATCH 24/34] fixed unused variable --- Rust/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 2c8d2b7..5455f6e 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -179,7 +179,7 @@ fn exchange_with_server(stream: TcpStream) { })() { Ok(name) => String::from(name), - Err(e) => { + Err(_) => { println!("{}", ">>> Login successful".green()); String::new() } From 7dfbf7c081adb7652dcfba87a8ae395d3f90222f Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 02:33:37 +0100 Subject: [PATCH 25/34] Updated TODO lists --- Rust/src/client.rs | 14 +++++++------- Rust/src/server.rs | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 5455f6e..1d61728 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -13,15 +13,15 @@ use self::chrono::Local; /* 0.1 [ ] -1.1 [ ] +1.1 [X] 1.2 [ ] -1.3 [ ] -1.4 [ ] -1.5 [ ] -1.6 [ ] +1.3 [X] +1.4 [X] +1.5 [X] +1.6 [X] 1.7 [X] 1.8 [X] -1.9 [ ] +1.9 [X] 2.1 [X] 2.2 [X] 3.1 [ ] // pas utile avec Rust @@ -33,7 +33,7 @@ use self::chrono::Local; */ -// TODO: Implement requests 1.x from protocol (connection currently is broken) +// TODO: Implement requests 0.1 and 1.2 // TODO: Limit usernames to ascii fn hash_name(name: &str) -> usize { diff --git a/Rust/src/server.rs b/Rust/src/server.rs index b78c021..0f9cf66 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -6,8 +6,7 @@ use std::sync::{Arc, Mutex, MutexGuard}; use std::collections::HashMap; use self::chrono::Local; -// TODO: implement requests 1.x from protocol -// TODO: forbid usernames already in use +// TODO: add server-side controls: display clients list, kick client, shutdown /////////////////////////////////////////////////////////////////////////////// // Evolution implementation protocole // From 5a56afb709cced10d392ec017e17efcacdadc1e3 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Fri, 23 Mar 2018 17:25:13 +0100 Subject: [PATCH 26/34] Check for name to be ascii characters only --- Rust/src/client.rs | 29 +++++++++++++---- Rust/src/server.rs | 77 +++++++++++++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 1d61728..d5230e1 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -34,7 +34,6 @@ use self::chrono::Local; */ // TODO: Implement requests 0.1 and 1.2 -// TODO: Limit usernames to ascii fn hash_name(name: &str) -> usize { let mut s = DefaultHasher::new(); @@ -50,7 +49,7 @@ fn get_entry() -> String { } fn get_name() -> String { - loop { + 'mainloop: loop { println!("{}", "Please enter your name:".yellow().dimmed()); let mut name = &*get_entry(); name = name.trim(); @@ -61,6 +60,17 @@ fn get_name() -> String { ); 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; @@ -193,6 +203,9 @@ fn exchange_with_server(stream: TcpStream) { 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!("{}", ">>> Login Successful <<<".green()); println!("Type /clients to get the list of users connected"); @@ -217,10 +230,12 @@ fn exchange_with_server(stream: TcpStream) { 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 w > msg.len() + spliced_input[i].len() + 1 { msg.push(' '); @@ -228,9 +243,10 @@ fn exchange_with_server(stream: TcpStream) { } else { if first_line == true { println!( - "{} {}:{}", + "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), name.color(COLORS[name_hash]), + " |".green(), msg.yellow().dimmed() ); first_line = false; @@ -249,9 +265,10 @@ fn exchange_with_server(stream: TcpStream) { if first_line == true { println!( - "{} {}:{}", + "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), name.color(COLORS[name_hash]), + " |".green(), msg.yellow().dimmed() ); } else { @@ -291,7 +308,7 @@ fn exchange_with_server(stream: TcpStream) { println!( "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), - " ------> ".green(), + " ------> ".green(), spliced_input[1].color(COLORS[name_hash]), " has joined".green() ) @@ -303,7 +320,7 @@ fn exchange_with_server(stream: TcpStream) { println!( "{}{}{}{}", date.format("[%H:%M:%S]").to_string().dimmed(), - " <------ ".red(), + " <------ ".red(), spliced_input[1].color(COLORS[name_hash]), " has left".red() ) diff --git a/Rust/src/server.rs b/Rust/src/server.rs index 0f9cf66..c9d5722 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -179,36 +179,15 @@ fn handle_client(stream: TcpStream, clients: Arc>) { if spliced_input.len() == 5 { if spliced_input[2] == "CONNECT" && spliced_input[3] == "USER" { let username = String::from(spliced_input[4]); - let mut used = false; - { - let lock = clients.lock().unwrap(); - for (_, entry) in (*lock).iter() { - if username == entry.0 { - used = true; - break; - } + let mut ascii_nick = true; + for c in username.chars() { + if !c.is_ascii() { + ascii_nick = false; + send!("NAME FAILURE"); + break; } } - if used == false { - send!("NAME OK"); - return Ok(username); - } else { - send!("NAME FAILURE"); - } - } else { - return Err(Error::new(ErrorKind::Other, "BAD REQ")); - } - } - - loop { - send!("NAME REQ"); - match receive!() { - 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]); + if ascii_nick { let mut used = false; { let lock = clients.lock().unwrap(); @@ -226,9 +205,49 @@ fn handle_client(stream: TcpStream, clients: Arc>) { send!("NAME FAILURE"); } } + } else { + return Err(Error::new(ErrorKind::Other, "BAD REQ")); + } + } + + loop { + send!("NAME REQ"); + match receive!() { + 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; + 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 { + send!("NAME OK"); + return Ok(username); + } else { + send!("NAME FAILURE"); + } + } + } } } - // return Ok(String::new()); } } })() From 517d66b11b0a22ea9d5f91f570247183dd35e264 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 14:54:40 +0200 Subject: [PATCH 27/34] Improved server-side logs --- Rust/src/server.rs | 78 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/Rust/src/server.rs b/Rust/src/server.rs index c9d5722..c7ebb49 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -167,6 +167,12 @@ fn handle_client(stream: TcpStream, clients: Arc>) { 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" @@ -183,6 +189,12 @@ fn handle_client(stream: TcpStream, clients: Arc>) { 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; } @@ -199,9 +211,21 @@ fn handle_client(stream: TcpStream, clients: Arc>) { } } 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"); } } @@ -211,9 +235,21 @@ fn handle_client(stream: TcpStream, clients: Arc>) { } 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")); @@ -223,6 +259,12 @@ fn handle_client(stream: TcpStream, clients: Arc>) { 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; } @@ -239,9 +281,21 @@ fn handle_client(stream: TcpStream, clients: Arc>) { } } 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"); } } @@ -255,7 +309,7 @@ fn handle_client(stream: TcpStream, clients: Arc>) { Ok(name) => name, Err(e) => { println!( - "{time} Client {addr} encountered an error: {err}", + "{time} client {addr} encountered an error: {err}", time = get_time(), addr = client, err = e @@ -290,11 +344,25 @@ fn handle_client(stream: TcpStream, clients: Arc>) { match input { "BYE" => { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "BYE" + ); send!("BYE"); return Ok(()); } - "PING" => send!("PONG"), + "PING" => { + println!( + "{time} to client {addr} : {message}", + time = get_time(), + addr = client, + message = "NAME FAILURE" + ); + send!("PONG"); + } "REQ CLIENTS" => { let mut lock = clients.lock().unwrap(); @@ -320,9 +388,11 @@ fn handle_client(stream: TcpStream, clients: Arc>) { } _ => { println!( - "{time} Sending: BAD REQ. Cause: {message}", + "{time} to client {addr} : \"{message}\", cause : {inmessage}", time = get_time(), - message = input + addr = client, + message = "BAD REQ", + inmessage = input ); send!("BAD REQ"); } From 0b26338659d94c493162d7ba18d1243b1a7e8483 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 15:08:11 +0200 Subject: [PATCH 28/34] Further logs improvements --- Rust/src/server.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Rust/src/server.rs b/Rust/src/server.rs index c7ebb49..63d713f 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -112,7 +112,16 @@ fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard) { if client == to { let stream = &entry.1; let mut writer = BufWriter::new(stream); - writeln!(writer, "LIST CLIENTS {}", clients).unwrap(); + let mut req = String::from("LIST CLIENTS "); + 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; } @@ -345,10 +354,11 @@ fn handle_client(stream: TcpStream, clients: Arc>) { match input { "BYE" => { println!( - "{time} to client {addr} : {message}", + "{time} to {nick}@{addr} : {message}", time = get_time(), addr = client, - message = "BYE" + message = "BYE", + nick = name ); send!("BYE"); return Ok(()); @@ -356,10 +366,11 @@ fn handle_client(stream: TcpStream, clients: Arc>) { "PING" => { println!( - "{time} to client {addr} : {message}", + "{time} to {nick}@{addr} : {message}", time = get_time(), addr = client, - message = "NAME FAILURE" + message = "NAME FAILURE", + nick = name ); send!("PONG"); } From 741dff019517616cf1a8ee072cf53e4a27838e72 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 16:24:38 +0200 Subject: [PATCH 29/34] minor function signature change --- Rust/src/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/src/server.rs b/Rust/src/server.rs index 63d713f..653572b 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -47,7 +47,7 @@ type UserMap = HashMap; // CODE // /////////////////////////////////////////////////////////////////////////////// -pub fn get_time() -> String { +fn get_time() -> String { let date = Local::now(); date.format("[%H:%M:%S]").to_string() } From c0e43b4a4696bbd38a148fefa82005307fde8191 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 16:31:53 +0200 Subject: [PATCH 30/34] Fixed request 4.2.2 in server and client --- Rust/src/client.rs | 14 ++++++++++---- Rust/src/server.rs | 6 ++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index d5230e1..e0f0d82 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -12,7 +12,7 @@ use self::chrono::Local; /* -0.1 [ ] +0.1 [X] 1.1 [X] 1.2 [ ] 1.3 [X] @@ -29,7 +29,7 @@ use self::chrono::Local; 4.1.1 [X] 4.1.2 [X] 4.2.1 [X] -4.2.2 [-] +4.2.2 [X] */ @@ -259,6 +259,7 @@ fn exchange_with_server(stream: TcpStream) { } msg = String::new(); #[allow(unused_assignments)] + #[allow(unused_assignments)] i = i - 1; } } @@ -296,8 +297,13 @@ fn exchange_with_server(stream: TcpStream) { return Ok("Ok"); } "LIST" => { - println!("{}", ">>>> LIST OF CLIENTS CONNECTED <<<<".bold().yellow()); - for i in 2..spliced_input.len() { + 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]); } } diff --git a/Rust/src/server.rs b/Rust/src/server.rs index 653572b..9edc66c 100644 --- a/Rust/src/server.rs +++ b/Rust/src/server.rs @@ -31,7 +31,7 @@ use self::chrono::Local; 4.1.1 [X] 4.1.2 [X] 4.2.1 [X] -4.2.2 [-] +4.2.2 [X] */ @@ -100,7 +100,9 @@ fn distribute_message( 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(), @@ -112,7 +114,7 @@ fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard) { if client == to { let stream = &entry.1; let mut writer = BufWriter::new(stream); - let mut req = String::from("LIST CLIENTS "); + let mut req = String::from(format!("{}{} ", "LIST CLIENTS ", num_client)); req.push_str(clients); println!( "{time} to {nick}@{addr} : {message}", From 3495b81d5f1b789151bd51867a2d3a6f18778ff8 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 16:33:15 +0200 Subject: [PATCH 31/34] Version update --- Rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml index 1a74bbb..f3b3de2 100644 --- a/Rust/Cargo.toml +++ b/Rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chat-reseau" -version = "0.5.2" +version = "0.5.5" authors = ["Lucien Cartier-Tilet "] [dependencies] From 5b60be1c2e4f2d3fce46374fb7be09bdfef797d2 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Sun, 25 Mar 2018 16:37:00 +0200 Subject: [PATCH 32/34] Updated TODO --- Rust/src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index e0f0d82..fb5e132 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -33,7 +33,7 @@ use self::chrono::Local; */ -// TODO: Implement requests 0.1 and 1.2 +// TODO: Implement requests 1.2 fn hash_name(name: &str) -> usize { let mut s = DefaultHasher::new(); From ba9d82b08692f358eedaaf259e12ee20de4d486c Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Mon, 26 Mar 2018 12:01:49 +0200 Subject: [PATCH 33/34] code more readable, cleaned up a bit, still ugly --- Rust/src/client.rs | 111 +++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/Rust/src/client.rs b/Rust/src/client.rs index fb5e132..18d73cf 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -42,6 +42,31 @@ fn hash_name(name: &str) -> usize { 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(); @@ -206,20 +231,17 @@ fn exchange_with_server(stream: TcpStream) { "BAD" => { println!("{}", "Bad request from client".red()); } - "WELCOME" => { - println!("{}", ">>> Login Successful <<<".green()); - println!("Type /clients to get the list of users connected"); - println!("Type /quit to disconnect and quit"); - } + "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 name = String::from(spliced_input[1]); let mut first_line = true; - - // Hashing name for color - let mut s = DefaultHasher::new(); - name.hash(&mut s); - let name_hash: usize = (s.finish() as usize) % COLORS.len(); + let name_hash = hash_name(spliced_input[1]) % COLORS.len(); // Formatting name let mut name = String::new(); @@ -237,48 +259,21 @@ fn exchange_with_server(stream: TcpStream) { // 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 { - if first_line == true { - println!( - "{}{}{}{}", - date.format("[%H:%M:%S]").to_string().dimmed(), - name.color(COLORS[name_hash]), - " |".green(), - msg.yellow().dimmed() - ); - first_line = false; - } else { - println!( - "{}{}", - " |".green(), - msg.yellow().dimmed() - ); - } - msg = String::new(); - #[allow(unused_assignments)] - #[allow(unused_assignments)] - i = i - 1; + print_line(&name, name_hash, &msg, &mut first_line, &COLORS); + msg = String::from(spliced_input[i]); } } - if first_line == true { - println!( - "{}{}{}{}", - date.format("[%H:%M:%S]").to_string().dimmed(), - name.color(COLORS[name_hash]), - " |".green(), - msg.yellow().dimmed() - ); - } else { - println!( - "{}{}", - " |".green(), - msg.yellow().dimmed() - ); - } + // 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() { @@ -286,16 +281,15 @@ fn exchange_with_server(stream: TcpStream) { 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"); - } + "BYE" => return Ok("Ok"), + "LIST" => { println!( "{}{}{}", @@ -308,34 +302,31 @@ fn exchange_with_server(stream: TcpStream) { } } "JOIN" => { - let date = Local::now(); - let name_hash: usize = hash_name(spliced_input[1].clone()) % COLORS.len(); + let date = get_time(); + let name_hash: usize = hash_name(spliced_input[1]) % COLORS.len(); println!( "{}{}{}{}", - date.format("[%H:%M:%S]").to_string().dimmed(), + date.dimmed(), " ------> ".green(), spliced_input[1].color(COLORS[name_hash]), " has joined".green() ) } "LOGOUT" => { - let date = Local::now(); - let name_hash: usize = hash_name(spliced_input[1].clone()) % COLORS.len(); + let date = get_time(); + let name_hash = hash_name(spliced_input[1]) % COLORS.len(); println!( "{}{}{}{}", - date.format("[%H:%M:%S]").to_string().dimmed(), + date.dimmed(), " <------ ".red(), spliced_input[1].color(COLORS[name_hash]), " has left".red() ) } - _ => { - println!("{}", input); - } + _ => println!("{}", input), } - // println!("{}", input); })() { Ok(_) => { From 39b7d75235b4b3006b61f2056a2c9d0ae31a0df4 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Mon, 26 Mar 2018 13:46:43 +0200 Subject: [PATCH 34/34] Automatic clients name request for client + version update --- Rust/Cargo.toml | 2 +- Rust/src/client.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Rust/Cargo.toml b/Rust/Cargo.toml index f3b3de2..9b090f2 100644 --- a/Rust/Cargo.toml +++ b/Rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chat-reseau" -version = "0.5.5" +version = "0.5.7" authors = ["Lucien Cartier-Tilet "] [dependencies] diff --git a/Rust/src/client.rs b/Rust/src/client.rs index 18d73cf..79d7449 100644 --- a/Rust/src/client.rs +++ b/Rust/src/client.rs @@ -114,7 +114,8 @@ fn get_name() -> String { 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();