184 lines
5.2 KiB
Rust
184 lines
5.2 KiB
Rust
use chrono::{DateTime, Duration, Local};
|
|
use std::{collections::HashMap, net::UdpSocket};
|
|
use log::debug;
|
|
|
|
const MAX_CLIENTS: usize = 10;
|
|
const CLIENT_TIMEOUT: u32 = 300;
|
|
const MY_SPEED: u8 = 30;
|
|
|
|
fn strip_header(msg: &[u8]) -> Vec<u8> {
|
|
//print_msg(msg);
|
|
let mut stripped = Vec::new();
|
|
stripped.push(msg[1] & 3);
|
|
stripped.append(msg[2..].to_vec().as_mut());
|
|
//print_msg(&stripped);
|
|
stripped.to_owned()
|
|
}
|
|
|
|
fn mopp(speed: u8, data: &[u8]) -> Vec<u8> {
|
|
let serial = 1u8;
|
|
let morse = HashMap::from([
|
|
(b'0', "-----"),
|
|
(b'1', ".----"),
|
|
(b'2', "..---"),
|
|
(b'3', "...--"),
|
|
(b'4', "....-"),
|
|
(b'5', "....."),
|
|
(b'6', "-...."),
|
|
(b'7', "--..."),
|
|
(b'8', "---.."),
|
|
(b'9', "----."),
|
|
(b'a', ".-"),
|
|
(b'b', "-..."),
|
|
(b'c', "-.-."),
|
|
(b'd', "-.."),
|
|
(b'e', "."),
|
|
(b'f', "..-."),
|
|
(b'g', "--."),
|
|
(b'h', "...."),
|
|
(b'i', ".."),
|
|
(b'j', ".---"),
|
|
(b'k', "-.-"),
|
|
(b'l', ".-.."),
|
|
(b'm', "--"),
|
|
(b'n', "-."),
|
|
(b'o', "---"),
|
|
(b'p', ".--."),
|
|
(b'q', "--.-"),
|
|
(b'r', ".-."),
|
|
(b's', "..."),
|
|
(b't', "-"),
|
|
(b'u', "..-"),
|
|
(b'v', "...-"),
|
|
(b'w', ".--"),
|
|
(b'x', "-..-"),
|
|
(b'y', "-.--"),
|
|
(b'z', "--.."),
|
|
(b'=', "-...-"),
|
|
(b'/', "-..-."),
|
|
(b'+', ".-.-."),
|
|
(b'-', "-....-"),
|
|
(b'.', ".-.-.-"),
|
|
(b',', "--..--"),
|
|
(b'?', "..--.."),
|
|
(b':', "---..."),
|
|
(b'!', "-.-.--"),
|
|
(b'\'', ".----."),
|
|
]);
|
|
|
|
let mut m = "01".to_owned(); // protocol version
|
|
m.push_str(&format!("{:06b}", serial));
|
|
m.push_str(&format!("{:06b}", speed));
|
|
|
|
for &char in data {
|
|
if char == b' ' {
|
|
continue;
|
|
}
|
|
|
|
for b in morse.get(&char).unwrap().as_bytes() {
|
|
if *b == b'.' {
|
|
m.push_str("01");
|
|
} else {
|
|
m.push_str("10");
|
|
}
|
|
}
|
|
|
|
m.push_str("00"); // EOC
|
|
}
|
|
|
|
m.replace_range(m.len() - 2.., "11"); // EOW
|
|
|
|
m = format!(
|
|
"{1:0<0$}",
|
|
(8 as f32 * (m.len() as f32 / 8 as f32).ceil()) as usize,
|
|
m
|
|
);
|
|
|
|
let mut res = Vec::<u8>::new();
|
|
for i in (0..m.len()).step_by(8) {
|
|
let value = u8::from_str_radix(&m[i..i + 8], 2).unwrap();
|
|
res.push(value);
|
|
}
|
|
|
|
res.to_owned()
|
|
}
|
|
|
|
fn broadcast(
|
|
socket: &UdpSocket,
|
|
receivers: &HashMap<String, DateTime<Local>>,
|
|
client: &str,
|
|
data: &[u8],
|
|
) {
|
|
for rec in receivers {
|
|
// Do not broadcast to origin
|
|
if rec.0 == client {
|
|
continue;
|
|
}
|
|
socket.send_to(&data, &rec.0).unwrap();
|
|
}
|
|
}
|
|
|
|
fn main() -> std::io::Result<()> {
|
|
env_logger::init();
|
|
|
|
let socket = UdpSocket::bind("0.0.0.0:7373")?;
|
|
socket
|
|
.set_read_timeout(None)
|
|
.expect("Could not set read timeout");
|
|
|
|
debug!("Morserino chat server started.");
|
|
|
|
let mut receivers2: HashMap<String, DateTime<Local>> = HashMap::new();
|
|
|
|
loop {
|
|
let mut buf = [0; 64];
|
|
let (number_of_bytes, src_addr) = socket.recv_from(&mut buf).expect("Didn't receive data");
|
|
let client_addr = src_addr.to_string();
|
|
let speed = buf[1] >> 2;
|
|
let data = &buf[0..number_of_bytes];
|
|
|
|
if receivers2.contains_key(&client_addr) {
|
|
debug!("Client known: {client_addr}");
|
|
if strip_header(data) == strip_header(mopp(speed, b":bye").as_slice()) {
|
|
debug!("Removing client {client_addr} as requested.");
|
|
socket
|
|
.send_to(mopp(speed, b":bye").as_slice(), &client_addr)
|
|
.unwrap();
|
|
receivers2.remove(&client_addr);
|
|
} else {
|
|
broadcast(&socket, &receivers2, &client_addr, data);
|
|
receivers2.insert(client_addr.to_owned(), Local::now());
|
|
}
|
|
} else if strip_header(data) == strip_header(mopp(speed, b"hi").as_slice()) {
|
|
debug!("Adding new client {client_addr}");
|
|
if receivers2.len() < MAX_CLIENTS {
|
|
receivers2.insert(client_addr.to_owned(), Local::now());
|
|
socket
|
|
.send_to(
|
|
mopp(speed, format!("{}{}", ":hi ", receivers2.len()).as_bytes())
|
|
.as_slice(),
|
|
&client_addr,
|
|
)
|
|
.unwrap();
|
|
} else {
|
|
debug!("There are already too many clients connected.");
|
|
socket
|
|
.send_to(mopp(speed, b":qrl").as_slice(), &client_addr)
|
|
.unwrap();
|
|
}
|
|
} else {
|
|
debug!("Ignoring unknown client {client_addr}");
|
|
}
|
|
|
|
let timestamp = Local::now();
|
|
for cl in &receivers2 {
|
|
if *cl.1 + Duration::seconds(CLIENT_TIMEOUT as i64) < timestamp {
|
|
debug!("Removing outdated client {}", cl.0);
|
|
socket
|
|
.send_to(mopp(MY_SPEED, b":bye").as_slice(), &client_addr)
|
|
.unwrap();
|
|
}
|
|
}
|
|
receivers2.retain(|_, val| *val + Duration::seconds(CLIENT_TIMEOUT as i64) >= timestamp);
|
|
}
|
|
}
|