Further improvements for the server and client

This commit is contained in:
Phuntsok Drak-pa 2018-03-21 20:38:20 +01:00
parent 50e4b6a380
commit 60bedb7ff6
3 changed files with 127 additions and 69 deletions

View File

@ -4,3 +4,5 @@ version = "0.2.0"
authors = ["Lucien Cartier-Tilet <drakpa@drakpa.fr>"] authors = ["Lucien Cartier-Tilet <drakpa@drakpa.fr>"]
[dependencies] [dependencies]
colored = "1.6"
chrono = "0.4"

View File

@ -1,7 +1,11 @@
extern crate chrono;
extern crate colored;
use std; use std;
use std::io::*; use std::io::*;
use std::net::TcpStream; use std::net::TcpStream;
use std::thread; use std::thread;
use self::colored::*;
use self::chrono::Local;
// static leave_msg: &str = "BYE"; // static leave_msg: &str = "BYE";
@ -12,12 +16,11 @@ fn get_entry() -> String {
buf.replace("\n", "").replace("\r", "") buf.replace("\n", "").replace("\r", "")
} }
fn write_to_server(stream: TcpStream) { fn get_name(writer: &mut BufWriter<&TcpStream>) {
let mut writer = BufWriter::new(&stream);
// entrée du nom d'utilisateur
loop { loop {
match &*get_entry() { let mut line = &*get_entry();
line = line.trim();
match line {
"" => { "" => {
continue; continue;
} }
@ -25,11 +28,10 @@ fn write_to_server(stream: TcpStream) {
println!("Disconnecting..."); println!("Disconnecting...");
writeln!(writer, "BYE").unwrap(); writeln!(writer, "BYE").unwrap();
writer.flush().unwrap(); writer.flush().unwrap();
println!("Disconnected!");
return (); return ();
} }
line => { 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(" ").collect();
let spliced: Vec<&str> = line_str.split_whitespace().collect(); let spliced: Vec<&str> = line_str.split_whitespace().collect();
if spliced.len() > 1 { if spliced.len() > 1 {
@ -40,19 +42,24 @@ fn write_to_server(stream: TcpStream) {
writer.flush().unwrap(); 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 { loop {
match &*get_entry() { let line = &*get_entry();
"" => { line.trim();
; match line {
} "" => {}
"/quit" => { "/quit" => {
println!("Disconnecting...");
writeln!(writer, "BYE").unwrap(); writeln!(writer, "BYE").unwrap();
writer.flush().unwrap(); writer.flush().unwrap();
println!("Disconnected!");
return (); return ();
} }
"/clients" => { "/clients" => {
@ -74,17 +81,18 @@ fn exchange_with_server(stream: TcpStream) {
let stream_cpy = stream.try_clone().unwrap(); let stream_cpy = stream.try_clone().unwrap();
let mut reader = BufReader::new(&stream_cpy); let mut reader = BufReader::new(&stream_cpy);
println!("Enter `/quit` when you want to leave");
macro_rules! receive { macro_rules! receive {
() => ({ () => ({
let mut line = String::new(); let mut line = String::new();
match reader.read_line(&mut line) { match reader.read_line(&mut line) {
Ok(len) => { Ok(len) => {
if len == 0 { if len == 0 {
// Reader is at EOF. Could use ErrorKind::UnexpectedEOF, but still unstable. // Reader is at EOF. Could use ErrorKind::UnexpectedEOF,
let ret = std::io::Error::new(std::io::ErrorKind::Other, "test"); // but still unstable.
return Err(ret): std::result::Result<&str, std::io::Error>; let ret = std::io::Error::new(
std::io::ErrorKind::Other, "test");
return
Err(ret): std::result::Result<&str,std::io::Error>;
} }
line.pop(); line.pop();
} }
@ -101,13 +109,50 @@ fn exchange_with_server(stream: TcpStream) {
match (|| loop { match (|| loop {
let input: String = String::from(receive!()); let input: String = String::from(receive!());
let spliced_input: Vec<&str> = input.split(" ").collect(); 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] { 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" => { "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); println!("{}", input);
@ -117,23 +162,22 @@ fn exchange_with_server(stream: TcpStream) {
})() })()
{ {
Ok(_) => { Ok(_) => {
println!("Left?"); println!("{}", ">>> Successfully left the room <<<".green());
} }
Err(_) => { Err(e) => {
println!(">>> Successfully left the room <<<"); println!("{}: {}", "Error: Function terminated too early".red(), e);
} }
} }
} }
pub fn client(server_address: String) { pub fn client(server_address: String) {
println!("Tentative de connexion a serveur..."); println!("Trying to connect to the server...");
match TcpStream::connect(server_address) { match TcpStream::connect(server_address) {
Ok(stream) => { Ok(stream) => {
println!("Connexion au serveur réussie !");
exchange_with_server(stream); exchange_with_server(stream);
} }
Err(e) => { Err(e) => {
println!("La connection au serveur a échoué : {}", e); println!("{} {}", "Connection to server failed:".red(), e);
return; return;
} }
} }

View File

@ -17,7 +17,7 @@ use std::collections::HashMap;
1.5 [ ] 1.5 [ ]
1.6 [ ] 1.6 [ ]
1.7 [X] 1.7 [X]
1.8 [ ] 1.8 [X]
1.9 [ ] 1.9 [ ]
2.1 [X] 2.1 [X]
2.2 [X] 2.2 [X]
@ -26,19 +26,28 @@ use std::collections::HashMap;
4.1.1 [X] 4.1.1 [X]
4.1.2 [X] 4.1.2 [X]
4.2.1 [X] 4.2.1 [X]
4.2.2 [X] 4.2.2 [-]
*/ */
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// code // // TYPES //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Map for all connected clients containing their name and stream // Map for all connected clients containing their name and stream
type UserMapValue = (String, TcpStream); type UserMapValue = (String, TcpStream);
type UserMap = HashMap<SocketAddr, UserMapValue>; type UserMap = HashMap<SocketAddr, UserMapValue>;
fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard<UserMap>) { ///////////////////////////////////////////////////////////////////////////////
// CODE //
///////////////////////////////////////////////////////////////////////////////
fn distribute_message(
msg: &str,
not_to: &SocketAddr,
lock: &mut MutexGuard<UserMap>,
everyone: bool,
) {
let mut name = String::new(); let mut name = String::new();
for (client, entry) in (*lock).iter() { for (client, entry) in (*lock).iter() {
if client == not_to { if client == not_to {
@ -47,29 +56,30 @@ fn distribute_message(msg: &str, not_to: &SocketAddr, lock: &mut MutexGuard<User
} }
} }
for (other_client, entry) in (*lock).iter() { for (other_client, entry) in (*lock).iter() {
if other_client != not_to { let other_name = &entry.0;
let other_name = &entry.0; let other_stream = &entry.1;
let other_stream = &entry.1; if everyone == false && other_client == not_to {
match (|| -> Result<()> { continue;
let mut writer = BufWriter::new(other_stream); }
// test if message begins with "MSG " ///////////////////////// match (|| -> Result<()> {
if &msg[..4] == "MSG " { let mut writer = BufWriter::new(other_stream);
try!(writeln!(writer, "FROM {} {}", name, msg)); // test if message begins with "MSG " /////////////////////////
} else { if &msg[..4] == "MSG " {
try!(writeln!(writer, "{}", msg)); try!(writeln!(writer, "FROM {} {}", name, msg));
} } else {
/////////////////////////////////////////////////////////////// try!(writeln!(writer, "{}", msg));
try!(writer.flush()); }
return Ok(()); ///////////////////////////////////////////////////////////////
})() try!(writer.flush());
{ return Ok(());
Ok(_) => {} })()
Err(e) => { {
println!( Ok(_) => {}
"Client {} <{}> disappeared during message distribution: {}", Err(e) => {
other_client, other_name, 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<User
fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard<UserMap>) { fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard<UserMap>) {
let mut clients = String::new(); let mut clients = String::new();
for (client, entry) in (*lock).iter() { for (client, entry) in (*lock).iter() {
if client != to { &entry.0.trim_left();
clients.push_str(&format!("{} ", &entry.0)); &entry.0.trim_right();
} clients.push_str(&format!(
"{}{} ",
if client == to { "(You)" } else { "" },
&entry.0
));
} }
clients.trim_left();
println!("{}", clients); println!("{}", clients);
for (client, entry) in (*lock).iter() { for (client, entry) in (*lock).iter() {
if client == to { if client == to {
let stream = &entry.1; let stream = &entry.1;
let mut writer = BufWriter::new(stream); let mut writer = BufWriter::new(stream);
writeln!(writer, "{}", clients).unwrap(); writeln!(writer, "LIST CLIENTS {}", clients).unwrap();
writer.flush().unwrap(); writer.flush().unwrap();
return; return;
} }
@ -96,7 +111,7 @@ fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard<UserMap>) {
fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard<UserMap>) { fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard<UserMap>) {
(*lock).remove(&client); (*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<Mutex<UserMap>>) { fn handle_client(stream: TcpStream, clients: Arc<Mutex<UserMap>>) {
@ -144,7 +159,6 @@ fn handle_client(stream: TcpStream, clients: Arc<Mutex<UserMap>>) {
send!("Please enter your name:"); send!("Please enter your name:");
let name = receive!(); let name = receive!();
println!("Client {} identified as {}", client, name); println!("Client {} identified as {}", client, name);
send!("DEBUG: You can now type messages. Leave this chat with the request `BYE`.");
Ok(name) Ok(name)
})() })()
{ {
@ -159,7 +173,7 @@ fn handle_client(stream: TcpStream, clients: Arc<Mutex<UserMap>>) {
{ {
let mut lock = clients.lock().unwrap(); let mut lock = clients.lock().unwrap();
(*lock).insert(client, (name.clone(), stream.try_clone().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(); writeln!(writer, "WELCOME").unwrap();
@ -176,16 +190,14 @@ fn handle_client(stream: TcpStream, clients: Arc<Mutex<UserMap>>) {
send!("PONG"); send!("PONG");
} }
"REQ CLIENTS" => { "REQ CLIENTS" => {
{ let mut lock = clients.lock().unwrap();
let mut lock = clients.lock().unwrap(); send_clients_name(&client, &mut lock);
send_clients_name(&client, &mut lock);
}
} }
input => { input => {
println!("{} <{}>: {}", client, name, input); println!("{} <{}>: {}", client, name, input);
{ {
let mut lock = clients.lock().unwrap(); let mut lock = clients.lock().unwrap();
distribute_message(&format!("{}", input), &client, &mut lock); distribute_message(&format!("{}", input), &client, &mut lock, true);
} }
} }
} }