Further improvements for the server and client
This commit is contained in:
		
							parent
							
								
									50e4b6a380
								
							
						
					
					
						commit
						60bedb7ff6
					
				@ -4,3 +4,5 @@ version = "0.2.0"
 | 
			
		||||
authors = ["Lucien Cartier-Tilet <drakpa@drakpa.fr>"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
colored = "1.6"
 | 
			
		||||
chrono = "0.4"
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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<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();
 | 
			
		||||
    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<User
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
                // 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<User
 | 
			
		||||
fn send_clients_name(to: &SocketAddr, lock: &mut MutexGuard<UserMap>) {
 | 
			
		||||
    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<UserMap>) {
 | 
			
		||||
 | 
			
		||||
fn disconnect_user(name: &str, client: &SocketAddr, lock: &mut MutexGuard<UserMap>) {
 | 
			
		||||
    (*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>>) {
 | 
			
		||||
@ -144,7 +159,6 @@ fn handle_client(stream: TcpStream, clients: Arc<Mutex<UserMap>>) {
 | 
			
		||||
        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<Mutex<UserMap>>) {
 | 
			
		||||
    {
 | 
			
		||||
        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<Mutex<UserMap>>) {
 | 
			
		||||
                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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user