Skip to content

Instantly share code, notes, and snippets.

@scttnlsn
Created November 15, 2019 20:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scttnlsn/e02f5e425068c66f4962b4bddfb74002 to your computer and use it in GitHub Desktop.
Save scttnlsn/e02f5e425068c66f4962b4bddfb74002 to your computer and use it in GitHub Desktop.
Rust chat server
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