mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-04 10:41:29 +03:00
Epoll (#20)
major update: - remove threading overhead by resorting to an event driven design with mio as a backend for epoll - Clients can now have an arbitrary amount of ip adresses and lan-mouse will automatically choose the correct one - -> seemless switching between ethernet and wifi - cli frontend + frontend adapter for future frontends
This commit is contained in:
committed by
GitHub
parent
22e6c531af
commit
1a4d0e05be
228
src/client.rs
228
src/client.rs
@@ -1,106 +1,190 @@
|
||||
use std::{net::SocketAddr, error::Error, fmt::Display, sync::{Arc, atomic::{AtomicBool, Ordering, AtomicU32}, RwLock}};
|
||||
use std::{net::SocketAddr, collections::{HashSet, hash_set::Iter}, fmt::Display, time::{Instant, Duration}, iter::Cloned};
|
||||
|
||||
use crate::{config::{self, DEFAULT_PORT}, dns};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Eq, Hash, PartialEq, Clone, Copy)]
|
||||
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum Position {
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Client {
|
||||
pub addr: SocketAddr,
|
||||
pub pos: Position,
|
||||
pub handle: ClientHandle,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn handle(&self) -> ClientHandle {
|
||||
return self.handle;
|
||||
impl Display for Position {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
Position::Left => "left",
|
||||
Position::Right => "right",
|
||||
Position::Top => "top",
|
||||
Position::Bottom => "bottom",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ClientEvent {
|
||||
Create(Client),
|
||||
Destroy(Client),
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Client {
|
||||
/// handle to refer to the client.
|
||||
/// This way any event consumer / producer backend does not
|
||||
/// need to know anything about a client other than its handle.
|
||||
pub handle: ClientHandle,
|
||||
/// `active` address of the client, used to send data to.
|
||||
/// This should generally be the socket address where data
|
||||
/// was last received from.
|
||||
pub active_addr: Option<SocketAddr>,
|
||||
/// all socket addresses associated with a particular client
|
||||
/// e.g. Laptops usually have at least an ethernet and a wifi port
|
||||
/// which have different ip addresses
|
||||
pub addrs: HashSet<SocketAddr>,
|
||||
/// position of a client on screen
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
pub struct ClientManager {
|
||||
next_id: AtomicU32,
|
||||
clients: RwLock<Vec<Client>>,
|
||||
subscribers: RwLock<Vec<Arc<AtomicBool>>>,
|
||||
pub enum ClientEvent {
|
||||
Create(ClientHandle, Position),
|
||||
Destroy(ClientHandle),
|
||||
UpdatePos(ClientHandle, Position),
|
||||
AddAddr(ClientHandle, SocketAddr),
|
||||
RemoveAddr(ClientHandle, SocketAddr),
|
||||
}
|
||||
|
||||
pub type ClientHandle = u32;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ClientConfigError;
|
||||
|
||||
impl Display for ClientConfigError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "neither ip nor hostname specified")
|
||||
}
|
||||
pub struct ClientManager {
|
||||
/// probably not beneficial to use a hashmap here
|
||||
clients: Vec<Client>,
|
||||
last_ping: Vec<(ClientHandle, Option<Instant>)>,
|
||||
last_seen: Vec<(ClientHandle, Option<Instant>)>,
|
||||
last_replied: Vec<(ClientHandle, Option<Instant>)>,
|
||||
next_client_id: u32,
|
||||
}
|
||||
|
||||
impl Error for ClientConfigError {}
|
||||
|
||||
impl ClientManager {
|
||||
fn add_client(&self, client: &config::Client, pos: Position) -> Result<(), Box<dyn Error>> {
|
||||
let ip = match client.ip {
|
||||
Some(ip) => ip,
|
||||
None => match &client.host_name {
|
||||
Some(host_name) => dns::resolve(host_name)?,
|
||||
None => return Err(Box::new(ClientConfigError{})),
|
||||
},
|
||||
};
|
||||
let addr = SocketAddr::new(ip, client.port.unwrap_or(DEFAULT_PORT));
|
||||
self.register_client(addr, pos);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn notify(&self) {
|
||||
for subscriber in self.subscribers.read().unwrap().iter() {
|
||||
subscriber.store(true, Ordering::SeqCst);
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
clients: vec![],
|
||||
next_client_id: 0,
|
||||
last_ping: vec![],
|
||||
last_seen: vec![],
|
||||
last_replied: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn new_id(&self) -> ClientHandle {
|
||||
let id = self.next_id.load(Ordering::Acquire);
|
||||
self.next_id.store(id + 1, Ordering::Release);
|
||||
id as ClientHandle
|
||||
/// add a new client to this manager
|
||||
pub fn add_client(&mut self, addrs: HashSet<SocketAddr>, pos: Position) -> ClientHandle {
|
||||
let handle = self.next_id();
|
||||
// we dont know, which IP is initially active
|
||||
let active_addr = None;
|
||||
|
||||
// store the client
|
||||
let client = Client { handle, active_addr, addrs, pos };
|
||||
self.clients.push(client);
|
||||
self.last_ping.push((handle, None));
|
||||
self.last_seen.push((handle, None));
|
||||
self.last_replied.push((handle, None));
|
||||
handle
|
||||
}
|
||||
|
||||
pub fn new(config: &config::Config) -> Result<Self, Box<dyn Error>> {
|
||||
|
||||
let client_manager = ClientManager {
|
||||
next_id: AtomicU32::new(0),
|
||||
clients: RwLock::new(Vec::new()),
|
||||
subscribers: RwLock::new(vec![]),
|
||||
};
|
||||
|
||||
// add clients from config
|
||||
for (client, pos) in config.clients.iter() {
|
||||
client_manager.add_client(&client, *pos)?;
|
||||
/// add a socket address to the given client
|
||||
pub fn add_addr(&mut self, client: ClientHandle, addr: SocketAddr) {
|
||||
if let Some(client) = self.get_mut(client) {
|
||||
client.addrs.insert(addr);
|
||||
}
|
||||
|
||||
Ok(client_manager)
|
||||
}
|
||||
|
||||
pub fn register_client(&self, addr: SocketAddr, pos: Position) {
|
||||
let handle = self.new_id();
|
||||
let client = Client { addr, pos, handle };
|
||||
self.clients.write().unwrap().push(client);
|
||||
self.notify();
|
||||
/// remove socket address from the given client
|
||||
pub fn remove_addr(&mut self, client: ClientHandle, addr: SocketAddr) {
|
||||
if let Some(client) = self.get_mut(client) {
|
||||
client.addrs.remove(&addr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clients(&self) -> Vec<Client> {
|
||||
self.clients.read().unwrap().clone()
|
||||
pub fn set_default_addr(&mut self, client: ClientHandle, addr: SocketAddr) {
|
||||
if let Some(client) = self.get_mut(client) {
|
||||
client.active_addr = Some(addr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&self, subscriber: Arc<AtomicBool>) {
|
||||
self.subscribers.write().unwrap().push(subscriber);
|
||||
/// update the position of a client
|
||||
pub fn update_pos(&mut self, client: ClientHandle, pos: Position) {
|
||||
if let Some(client) = self.get_mut(client) {
|
||||
client.pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_active_addr(&self, client: ClientHandle) -> Option<SocketAddr> {
|
||||
self.get(client)?.active_addr
|
||||
}
|
||||
|
||||
pub fn get_addrs(&self, client: ClientHandle) -> Option<Cloned<Iter<'_, SocketAddr>>> {
|
||||
Some(self.get(client)?.addrs.iter().cloned())
|
||||
}
|
||||
|
||||
pub fn last_ping(&self, client: ClientHandle) -> Option<Duration> {
|
||||
let last_ping = self.last_ping
|
||||
.iter()
|
||||
.find(|(c,_)| *c == client)
|
||||
.unwrap().1;
|
||||
last_ping.map(|p| p.elapsed())
|
||||
}
|
||||
|
||||
pub fn last_seen(&self, client: ClientHandle) -> Option<Duration> {
|
||||
let last_seen = self.last_seen
|
||||
.iter()
|
||||
.find(|(c, _)| *c == client)
|
||||
.unwrap().1;
|
||||
last_seen.map(|t| t.elapsed())
|
||||
}
|
||||
|
||||
pub fn last_replied(&self, client: ClientHandle) -> Option<Duration> {
|
||||
let last_replied = self.last_replied
|
||||
.iter()
|
||||
.find(|(c, _)| *c == client)
|
||||
.unwrap().1;
|
||||
last_replied.map(|t| t.elapsed())
|
||||
}
|
||||
|
||||
pub fn reset_last_ping(&mut self, client: ClientHandle) {
|
||||
self.last_ping
|
||||
.iter_mut()
|
||||
.find(|(c, _)| *c == client)
|
||||
.unwrap().1 = Some(Instant::now());
|
||||
}
|
||||
|
||||
pub fn reset_last_seen(&mut self, client: ClientHandle) {
|
||||
self.last_seen
|
||||
.iter_mut()
|
||||
.find(|(c, _)| *c == client)
|
||||
.unwrap().1 = Some(Instant::now());
|
||||
}
|
||||
|
||||
pub fn reset_last_replied(&mut self, client: ClientHandle) {
|
||||
self.last_replied
|
||||
.iter_mut()
|
||||
.find(|(c, _)| *c == client)
|
||||
.unwrap().1 = Some(Instant::now());
|
||||
}
|
||||
|
||||
pub fn get_client(&self, addr: SocketAddr) -> Option<ClientHandle> {
|
||||
self.clients
|
||||
.iter()
|
||||
.find(|c| c.addrs.contains(&addr))
|
||||
.map(|c| c.handle)
|
||||
}
|
||||
|
||||
fn next_id(&mut self) -> ClientHandle {
|
||||
let handle = self.next_client_id;
|
||||
self.next_client_id += 1;
|
||||
handle
|
||||
}
|
||||
|
||||
fn get<'a>(&'a self, client: ClientHandle) -> Option<&'a Client> {
|
||||
self.clients
|
||||
.iter()
|
||||
.find(|c| c.handle == client)
|
||||
}
|
||||
|
||||
fn get_mut<'a>(&'a mut self, client: ClientHandle) -> Option<&'a mut Client> {
|
||||
self.clients
|
||||
.iter_mut()
|
||||
.find(|c| c.handle == client)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user