Initial commit
More or less working chat server pre ChatServer refactor
This commit is contained in:
commit
f5099e6a70
|
@ -0,0 +1,304 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.0-rc.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7843ae7a539bef687e018bf9edf7e87728024b29d02b0f8409726be8880ae1a"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.0-rc.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae3cc2f259ea636871f5da15b0ac033f1821d7a5506c3d1bfbdde201f14c803"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "retain_mut"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11000e6ba5020e53e7cc26f73b91ae7d5496b4977851479edb66b694c0675c21"
|
||||
|
||||
[[package]]
|
||||
name = "rustchat"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"retain_mut",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "rustchat"
|
||||
authors = ["seba"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "3.0.0-rc.9", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
retain_mut = "0.1.5"
|
|
@ -0,0 +1,158 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::thread;
|
||||
use clap::Parser;
|
||||
use std::io::Write;
|
||||
use std::io::{self, BufRead};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use retain_mut::RetainMut;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
||||
fn send_to_all(clients: &Arc<Mutex<Vec<ChatUser>>>, msg: &str) -> Result<(), ChatError> {
|
||||
let now: DateTime<Utc> = Utc::now();
|
||||
let time_msg = format!("[{}] {}", now.format("%H:%M:%S"), msg);
|
||||
let mut broken_clients = vec![];
|
||||
clients.lock()?.borrow_mut().retain_mut(
|
||||
| client | {
|
||||
if write!(client.socket, "{}\n", time_msg).is_err() {
|
||||
broken_clients.push(client.name.clone());
|
||||
return false
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
for name in broken_clients {
|
||||
send_to_all(clients, &format!("* {} left the chat (broken pipe)", name))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Represent one single chat user and their network stuff
|
||||
struct ChatUser {
|
||||
/// user id
|
||||
id: u64,
|
||||
|
||||
/// Name of the chat user
|
||||
name: String,
|
||||
|
||||
/// TCPStream socket object thingy
|
||||
socket: TcpStream,
|
||||
}
|
||||
|
||||
impl ChatUser {
|
||||
pub fn new(id: u64, name: String, socket: TcpStream) -> Self {
|
||||
Self{id, name, socket}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatServer {
|
||||
user_map: BTreeMap<u64, ChatUser>,
|
||||
user_id_counter: u64,
|
||||
}
|
||||
|
||||
impl ChatServer {
|
||||
pub fn new() -> Self {
|
||||
Self{user_map: BtreeMap::new(), user_id_counter: 1}
|
||||
}
|
||||
|
||||
// register a client by creating a ChatUser for it and assigning it a user id
|
||||
pub fn register(&mut self, name: String, socket: TcpStream) {
|
||||
let chat_user = ChatUser::new(user_id_counter, name, socket);
|
||||
self.user_map.insert(chat_user.id, chat_user);
|
||||
self.user_id_counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ChatError {
|
||||
IOError(std::io::Error),
|
||||
MutexPoisonError(),
|
||||
Protocol(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ChatError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
ChatError::IOError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a, T> From<std::sync::PoisonError<std::sync::MutexGuard<'a, T>>> for ChatError {
|
||||
fn from(_error: std::sync::PoisonError<std::sync::MutexGuard<'a, T>>) -> Self {
|
||||
ChatError::MutexPoisonError()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_client(mut stream: TcpStream, clients: Arc<Mutex<Vec<ChatUser>>>) -> Result<(), ChatError> {
|
||||
let mut writer = stream.try_clone()?;
|
||||
let reader = io::BufReader::new(&mut stream);
|
||||
let mut lines = reader.lines();
|
||||
|
||||
// greet and recv nick
|
||||
write!(writer, "Hello and welcome to RustChat!\nNick: ")?;
|
||||
let nick = lines.next().ok_or(ChatError::Protocol(String::from("Could not recv nickname")))??;
|
||||
// FIXME: check for nick name uniqueness
|
||||
|
||||
// print joined msg and register at server
|
||||
send_to_all(&clients, &format!("* {} has joined the chat", nick))?;
|
||||
let curr_users = clients.lock()?.borrow().len();
|
||||
let users_in_room_msg = if curr_users == 1 { String::from("is 1 user") } else { format!("are {} users", curr_users) };
|
||||
write!(writer, "Welcome, {}! You can now start to chat! There {} in the room.\n", nick, users_in_room_msg)?;
|
||||
clients.lock()?.borrow_mut().push(ChatUser::new(nick.clone(), writer));
|
||||
|
||||
// read lines
|
||||
for line in lines {
|
||||
let msg_raw = line?;
|
||||
if msg_raw.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let msg = format!("<{}> {}", nick, msg_raw);
|
||||
send_to_all(&clients, &msg)?;
|
||||
println!("{}", msg);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn run_thread(stream: TcpStream, clients: Arc<Mutex<Vec<ChatUser>>>) {
|
||||
let quit_msg = match handle_client(stream, clients) {
|
||||
Ok(()) => String::from("Connection closed by user's choice"),
|
||||
Err(err) => format!("Client foobar't their thread: {:?}", err),
|
||||
};
|
||||
println!(" * Client has left the chat ({})", quit_msg);
|
||||
}
|
||||
|
||||
|
||||
/// Chatserver in RUST!
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(about, version, author)]
|
||||
struct Args {
|
||||
/// IP address to bind to
|
||||
#[clap(long, default_value = "0.0.0.0")]
|
||||
host: String,
|
||||
|
||||
/// Port to run the server on
|
||||
#[clap(short, long, default_value_t = 1337)]
|
||||
port: i16,
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let args = Args::parse();
|
||||
let binding_host = format!("{}:{}", args.host, args.port);
|
||||
println!("Binding to {}", binding_host);
|
||||
|
||||
let listener = TcpListener::bind(binding_host)?;
|
||||
let clients = Arc::new(Mutex::new(vec!()));
|
||||
|
||||
// accept connections and process them serially
|
||||
for stream in listener.incoming() {
|
||||
if let Ok(stream) = stream {
|
||||
let clients_clone = Arc::clone(&clients);
|
||||
thread::spawn(move || { run_thread(stream, clients_clone); });
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue