Created
November 15, 2019 20:26
-
-
Save scttnlsn/e02f5e425068c66f4962b4bddfb74002 to your computer and use it in GitHub Desktop.
Rust chat server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::hash_map::{Entry, HashMap}; | |
use std::io::{self, BufRead, BufReader, Write}; | |
use std::net::{TcpListener, TcpStream, ToSocketAddrs}; | |
use std::sync::{ | |
Arc, | |
mpsc::{self, Sender, Receiver}, | |
}; | |
use std::thread::{self}; | |
#[derive(Debug)] | |
enum Event { | |
Join { | |
name: String, | |
stream: Arc<TcpStream>, | |
}, | |
Leave { | |
name: String, | |
}, | |
Message { | |
from: String, | |
to: String, | |
content: String, | |
}, | |
} | |
fn send_loop(stream: Arc<TcpStream>, messages: Receiver<String>) -> io::Result<()> { | |
let mut stream = &*stream; | |
for message in messages { | |
stream.write_all(message.as_bytes())?; | |
stream.flush().unwrap(); | |
} | |
Ok(()) | |
} | |
fn client_loop(mut stream: TcpStream, events: Sender<Event>) { | |
stream.write_all("name: ".as_bytes()).unwrap(); | |
let stream = Arc::new(stream); | |
let reader = BufReader::new(&*stream); | |
let mut lines = reader.lines(); | |
let name = match lines.next() { | |
None => { | |
return; | |
}, | |
Some(line) => { | |
line.unwrap() | |
} | |
}; | |
events.send(Event::Join { | |
name: name.to_string(), | |
stream: Arc::clone(&stream), | |
}).unwrap(); | |
for line in lines { | |
let line = line.unwrap(); | |
let (to, content) = match line.find(':') { | |
None => ("", line[0..].trim()), | |
Some(i) => (&line[..i], line[i + 1..].trim()), | |
}; | |
events.send(Event::Message { | |
from: name.to_string(), | |
to: to.to_string(), | |
content: content.to_string(), | |
}).unwrap(); | |
} | |
events.send(Event::Leave { | |
name: name.to_string(), | |
}).unwrap(); | |
} | |
fn listen(addr: impl ToSocketAddrs, events: Sender<Event>) -> io::Result<()> { | |
let listener = TcpListener::bind(addr)?; | |
for stream in listener.incoming() { | |
let tx = events.clone(); | |
thread::spawn(move || { | |
client_loop(stream.unwrap(), tx); | |
}); | |
} | |
Ok(()) | |
} | |
fn process_events(events: Receiver<Event>) { | |
let mut clients: HashMap<String, Sender<String>> = HashMap::new(); | |
loop { | |
let event = events.recv().unwrap(); | |
match event { | |
Event::Join { name, stream } => { | |
println!("{:} joined", name); | |
for (_, sender) in &clients { | |
let line = format!("{:} joined\n", name); | |
sender.send(line).unwrap(); | |
} | |
let (sender, receiver) = mpsc::channel(); | |
match clients.entry(name) { | |
Entry::Occupied(_) => { | |
}, | |
Entry::Vacant(entry) => { | |
entry.insert(sender); | |
thread::spawn(move || { | |
send_loop(stream, receiver).unwrap(); | |
}); | |
} | |
} | |
}, | |
Event::Leave { name } => { | |
for (_, sender) in &clients { | |
let line = format!("{:} left\n", name); | |
sender.send(line).unwrap(); | |
} | |
}, | |
Event::Message { from, to, content } => { | |
match clients.get(&to) { | |
Some(sender) => { | |
let line = format!("from {:}: {:}\n", from, content); | |
sender.send(line).unwrap(); | |
}, | |
None => { | |
} | |
} | |
}, | |
} | |
} | |
} | |
fn main() { | |
let (tx, rx) = mpsc::channel(); | |
let events = tx.clone(); | |
let listener = thread::spawn(move || { | |
listen("127.0.0.1:8080", events).unwrap(); | |
}); | |
let event_processor = thread::spawn(move || { | |
process_events(rx); | |
}); | |
listener.join().unwrap(); | |
event_processor.join().unwrap(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment