use std::{
io::{ErrorKind, Read, Write},
net::{TcpListener, ToSocketAddrs},
prelude::v1::*,
sync::mpsc::{channel, Receiver, Sender},
thread::{self, JoinHandle},
};
#[derive(Debug)]
pub struct TcpServer {
#[allow(dead_code)]
server_handle: JoinHandle<()>,
outgoing: Sender<Outgoing>,
incoming: Receiver<Incoming>,
connected: bool,
}
enum Outgoing {
Message(String),
Kill,
}
#[derive(Debug)]
enum Incoming {
Message(String),
Connected,
Disconnected,
}
impl TcpServer {
pub fn new<A: ToSocketAddrs + Send + Sync + Clone + 'static>(address: A) -> Self {
let (outgoing, rx) = channel::<Outgoing>();
let (tx, incoming) = channel::<Incoming>();
let server_handle = thread::spawn(move || loop {
let (mut stream, _) = TcpListener::bind(address.clone())
.unwrap()
.accept()
.expect("Couldn't connect");
while rx.try_recv().is_ok() {}
tx.send(Incoming::Connected).unwrap();
stream.set_nonblocking(true).unwrap();
let mut buffer = [0; 1024];
loop {
match stream.read(&mut buffer) {
Ok(bytes_read) => {
let message = String::from_utf8(buffer[..bytes_read].to_vec()).unwrap();
let message = message.trim_end_matches('\0');
match message {
"ping" => {}
message => {
tx.send(Incoming::Message(message.to_string())).unwrap();
}
}
}
Err(err) => match err.kind() {
ErrorKind::WouldBlock => {}
ErrorKind::ConnectionReset => {
tx.send(Incoming::Disconnected).unwrap();
break;
}
kind => panic!("Unexpected error: {:?}", kind),
},
}
let mut end_loop = false;
while let Ok(message) = rx.try_recv() {
match message {
Outgoing::Message(message) => {
stream.write_all(message.as_bytes()).unwrap();
stream.write_all(&[0]).unwrap();
}
Outgoing::Kill => {
stream.write_all(b"kill\0").unwrap();
end_loop = true;
break;
}
}
}
if end_loop {
break;
}
}
let _ = stream.shutdown(std::net::Shutdown::Both);
});
Self {
server_handle,
outgoing,
incoming,
connected: false,
}
}
pub fn send_message(&self, msg: String) {
self.outgoing.send(Outgoing::Message(msg)).unwrap();
}
pub fn wait_to_connect(&mut self) {
for msg in self.incoming.iter() {
if let Incoming::Connected = msg {
self.connected = true;
break;
}
}
}
pub fn read_messages(&mut self) -> impl Iterator<Item = String> + '_ {
self.incoming.try_iter().filter_map(|v| match v {
Incoming::Message(v) => Some(v),
Incoming::Connected => {
self.connected = true;
None
}
Incoming::Disconnected => {
self.connected = false;
None
}
})
}
pub fn shutdown(self) {
self.outgoing.send(Outgoing::Kill).unwrap();
}
pub fn is_connected(&self) -> bool {
self.connected
}
}