mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 11:59:59 +03:00
support for cmdline args and better error handling (#4)
* support for cmdline args and better error handling * make config.toml optional * dont abuse panic for error handling * update doc * more panics removed * more unwraps removed
This commit is contained in:
committed by
GitHub
parent
deb1548e21
commit
a2d2e904f8
16
DOC.md
16
DOC.md
@@ -68,16 +68,10 @@ on the server level.
|
|||||||
## Device State - Active and Inactive
|
## Device State - Active and Inactive
|
||||||
To solve this problem, each device can be in exactly two states:
|
To solve this problem, each device can be in exactly two states:
|
||||||
|
|
||||||
Events can only be sent to active clients.
|
Either events are sent or received.
|
||||||
Events can only be received from inactive clients.
|
|
||||||
|
|
||||||
Active denotes that a particular device is controlled by the local pc.
|
This ensures that
|
||||||
|
- a) Events can never result in a feedback loop.
|
||||||
|
- b) As soon as a virtual input enters another client, lan-mouse will stop receiving events,
|
||||||
|
which ensures clients can only be controlled directly and not indirectly through other clients.
|
||||||
|
|
||||||
Any event received from an active device is ignored unless it is a state change request.
|
|
||||||
In this case the device is marked as inactive and no further events are sent to the device.
|
|
||||||
|
|
||||||
The received events are then processed until a further state change to active
|
|
||||||
is requested (when the corresponding layer surface is entered).
|
|
||||||
|
|
||||||
**In short:** The invariance "each client either sends or receives events" must
|
|
||||||
always be true.
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
port = 42069
|
port = 42069
|
||||||
backend = "wlroots"
|
backend = "wlroots"
|
||||||
|
|
||||||
# [client.right]
|
# [right]
|
||||||
# host_name = "localhost"
|
# host_name = "localhost"
|
||||||
# port = 42068
|
# port = 42068
|
||||||
|
|
||||||
[client.left]
|
[left]
|
||||||
host_name = "Osmium"
|
host_name = "Osmium"
|
||||||
ip = "192.168.178.114"
|
ip = "192.168.178.114"
|
||||||
port = 42069
|
port = 42069
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::net::SocketAddr;
|
use std::{net::SocketAddr, error::Error, fmt::Display};
|
||||||
|
|
||||||
use crate::{config, dns};
|
use crate::{config, dns};
|
||||||
|
|
||||||
@@ -35,20 +35,29 @@ pub struct ClientManager {
|
|||||||
|
|
||||||
pub type ClientHandle = u32;
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ClientConfigError {}
|
||||||
|
|
||||||
impl ClientManager {
|
impl ClientManager {
|
||||||
fn add_client(&mut self, client: &config::Client, pos: Position) {
|
fn add_client(&mut self, client: &config::Client, pos: Position) -> Result<(), Box<dyn Error>> {
|
||||||
let ip = match client.ip {
|
let ip = match client.ip {
|
||||||
Some(ip) => ip,
|
Some(ip) => ip,
|
||||||
None => match &client.host_name {
|
None => match &client.host_name {
|
||||||
Some(host_name) => match dns::resolve(host_name) {
|
Some(host_name) => dns::resolve(host_name)?,
|
||||||
Ok(ip) => ip,
|
None => return Err(Box::new(ClientConfigError{})),
|
||||||
Err(e) => panic!("{}", e),
|
|
||||||
},
|
|
||||||
None => panic!("neither ip nor hostname specified"),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let addr = SocketAddr::new(ip, client.port.unwrap_or(42069));
|
let addr = SocketAddr::new(ip, client.port.unwrap_or(42069));
|
||||||
self.register_client(addr, pos);
|
self.register_client(addr, pos);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_id(&mut self) -> ClientHandle {
|
fn new_id(&mut self) -> ClientHandle {
|
||||||
@@ -56,7 +65,7 @@ impl ClientManager {
|
|||||||
self.next_id
|
self.next_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(config: &config::Config) -> Self {
|
pub fn new(config: &config::Config) -> Result<Self, Box<dyn Error>> {
|
||||||
|
|
||||||
let mut client_manager = ClientManager {
|
let mut client_manager = ClientManager {
|
||||||
next_id: 0,
|
next_id: 0,
|
||||||
@@ -64,25 +73,11 @@ impl ClientManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// add clients from config
|
// add clients from config
|
||||||
for client in vec![
|
for (client, pos) in config.clients.iter() {
|
||||||
&config.client.left,
|
client_manager.add_client(&client, *pos)?;
|
||||||
&config.client.right,
|
|
||||||
&config.client.top,
|
|
||||||
&config.client.bottom,
|
|
||||||
] {
|
|
||||||
if let Some(client) = client {
|
|
||||||
let pos = match client {
|
|
||||||
client if Some(client) == config.client.left.as_ref() => Position::Left,
|
|
||||||
client if Some(client) == config.client.right.as_ref() => Position::Right,
|
|
||||||
client if Some(client) == config.client.top.as_ref() => Position::Top,
|
|
||||||
client if Some(client) == config.client.bottom.as_ref() => Position::Bottom,
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
client_manager.add_client(client, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client_manager
|
Ok(client_manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_client(&mut self, addr: SocketAddr, pos: Position) {
|
pub fn register_client(&mut self, addr: SocketAddr, pos: Position) {
|
||||||
|
|||||||
103
src/config.rs
103
src/config.rs
@@ -1,17 +1,17 @@
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use core::fmt;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::{error::Error, fs};
|
use std::{error::Error, fs};
|
||||||
|
|
||||||
|
use std::env;
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
use crate::client::Position;
|
||||||
pub struct Config {
|
|
||||||
pub client: Clients,
|
|
||||||
pub port: Option<u16>,
|
|
||||||
pub backend: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Clients {
|
pub struct ConfigToml {
|
||||||
|
pub port: Option<u16>,
|
||||||
|
pub backend: Option<String>,
|
||||||
pub left: Option<Client>,
|
pub left: Option<Client>,
|
||||||
pub right: Option<Client>,
|
pub right: Option<Client>,
|
||||||
pub top: Option<Client>,
|
pub top: Option<Client>,
|
||||||
@@ -25,9 +25,94 @@ pub struct Client {
|
|||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
#[derive(Debug, Clone)]
|
||||||
pub fn new(path: &str) -> Result<Config, Box<dyn Error>> {
|
struct MissingParameter {
|
||||||
|
arg: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MissingParameter {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Missing a parameter for argument: {}", self.arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MissingParameter {}
|
||||||
|
|
||||||
|
impl ConfigToml {
|
||||||
|
pub fn new(path: &str) -> Result<ConfigToml, Box<dyn Error>> {
|
||||||
let config = fs::read_to_string(path)?;
|
let config = fs::read_to_string(path)?;
|
||||||
Ok(toml::from_str::<_>(&config)?)
|
Ok(toml::from_str::<_>(&config)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_arg(key: &'static str) -> Result<Option<String>, MissingParameter> {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if arg != key {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match args.get(i+1) {
|
||||||
|
None => return Err(MissingParameter { arg: key }),
|
||||||
|
Some(arg) => return Ok(Some(arg.clone())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub backend: Option<String>,
|
||||||
|
pub port: u16,
|
||||||
|
pub clients: Vec<(Client, Position)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let config_path = "config.toml";
|
||||||
|
let config_toml = match ConfigToml::new(config_path) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("config.toml: {e}");
|
||||||
|
eprintln!("Continuing without config file ...");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(c) => Some(c),
|
||||||
|
};
|
||||||
|
|
||||||
|
let backend = match find_arg("--backend")? {
|
||||||
|
None => match &config_toml {
|
||||||
|
Some(c) => c.backend.clone(),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
backend => backend,
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_PORT: u16 = 4242;
|
||||||
|
|
||||||
|
let port = match find_arg("--port")? {
|
||||||
|
Some(port) => port.parse::<u16>()?,
|
||||||
|
None => match &config_toml {
|
||||||
|
Some(c) => c.port.unwrap_or(DEFAULT_PORT),
|
||||||
|
None => DEFAULT_PORT,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut clients: Vec<(Client, Position)> = vec![];
|
||||||
|
|
||||||
|
if let Some(config_toml) = config_toml {
|
||||||
|
if let Some(c) = config_toml.right {
|
||||||
|
clients.push((c, Position::Right))
|
||||||
|
}
|
||||||
|
if let Some(c) = config_toml.left {
|
||||||
|
clients.push((c, Position::Left))
|
||||||
|
}
|
||||||
|
if let Some(c) = config_toml.top {
|
||||||
|
clients.push((c, Position::Top))
|
||||||
|
}
|
||||||
|
if let Some(c) = config_toml.bottom {
|
||||||
|
clients.push((c, Position::Bottom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Config { backend, clients, port })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::{thread::{JoinHandle, self}, sync::mpsc::Receiver};
|
use std::{thread::{JoinHandle, self}, sync::mpsc::Receiver, error::Error};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -14,11 +14,11 @@ enum Backend {
|
|||||||
Libei,
|
Libei,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec<Client>, backend: Option<String>) -> JoinHandle<()> {
|
pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec<Client>, backend: Option<String>) -> Result<JoinHandle<()>, Box<dyn Error>> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _backend = backend;
|
let _backend = backend;
|
||||||
|
|
||||||
thread::Builder::new()
|
Ok(thread::Builder::new()
|
||||||
.name("event consumer".into())
|
.name("event consumer".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -72,6 +72,5 @@ pub fn start(consume_rx: Receiver<(Event, ClientHandle)>, clients: Vec<Client>,
|
|||||||
consumer::x11::run(consume_rx, clients);
|
consumer::x11::run(consume_rx, clients);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})?)
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(port: u16) -> Self {
|
pub fn new(port: u16) -> Result<Self, Box<dyn Error>> {
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
|
let listen_addr = SocketAddr::new("0.0.0.0".parse()?, port);
|
||||||
let sending = Arc::new(AtomicBool::new(false));
|
let sending = Arc::new(AtomicBool::new(false));
|
||||||
Server {
|
Ok(Server {
|
||||||
listen_addr,
|
listen_addr,
|
||||||
sending,
|
sending,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
@@ -36,7 +36,7 @@ impl Server {
|
|||||||
consume_tx: SyncSender<(Event, ClientHandle)>,
|
consume_tx: SyncSender<(Event, ClientHandle)>,
|
||||||
) -> Result<(JoinHandle<()>, JoinHandle<()>), Box<dyn Error>> {
|
) -> Result<(JoinHandle<()>, JoinHandle<()>), Box<dyn Error>> {
|
||||||
let udp_socket = UdpSocket::bind(self.listen_addr)?;
|
let udp_socket = UdpSocket::bind(self.listen_addr)?;
|
||||||
let rx = udp_socket.try_clone().unwrap();
|
let rx = udp_socket.try_clone()?;
|
||||||
let tx = udp_socket;
|
let tx = udp_socket;
|
||||||
|
|
||||||
let sending = self.sending.clone();
|
let sending = self.sending.clone();
|
||||||
@@ -92,8 +92,7 @@ impl Server {
|
|||||||
.expect("event consumer unavailable");
|
.expect("event consumer unavailable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sending = self.sending.clone();
|
let sending = self.sending.clone();
|
||||||
|
|
||||||
@@ -124,8 +123,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})?;
|
||||||
.unwrap();
|
|
||||||
Ok((receiver, sender))
|
Ok((receiver, sender))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
76
src/main.rs
76
src/main.rs
@@ -1,4 +1,4 @@
|
|||||||
use std::sync::mpsc;
|
use std::{sync::mpsc, process, env};
|
||||||
|
|
||||||
use lan_mouse::{
|
use lan_mouse::{
|
||||||
client::ClientManager,
|
client::ClientManager,
|
||||||
@@ -6,12 +6,24 @@ use lan_mouse::{
|
|||||||
config, event, request,
|
config, event, request,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn usage() {
|
||||||
|
eprintln!("usage: {} [--backend <backend>] [--port <port>]",
|
||||||
|
env::args().next().unwrap_or("lan-mouse".into()));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// parse config file
|
// parse config file
|
||||||
let config = config::Config::new("config.toml").unwrap();
|
let config = match config::Config::new() {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
usage();
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Ok(config) => config,
|
||||||
|
};
|
||||||
|
|
||||||
// port or default
|
// port or default
|
||||||
let port = config.port.unwrap_or(42069);
|
let port = config.port;
|
||||||
|
|
||||||
// event channel for producing events
|
// event channel for producing events
|
||||||
let (produce_tx, produce_rx) = mpsc::sync_channel(128);
|
let (produce_tx, produce_rx) = mpsc::sync_channel(128);
|
||||||
@@ -20,26 +32,64 @@ pub fn main() {
|
|||||||
let (consume_tx, consume_rx) = mpsc::sync_channel(128);
|
let (consume_tx, consume_rx) = mpsc::sync_channel(128);
|
||||||
|
|
||||||
// create client manager
|
// create client manager
|
||||||
let mut client_manager = ClientManager::new(&config);
|
let mut client_manager = match ClientManager::new(&config) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
Ok(m) => m,
|
||||||
|
};
|
||||||
|
|
||||||
// start receiving client connection requests
|
// start receiving client connection requests
|
||||||
let (request_server, request_thread) = request::Server::listen(port).unwrap();
|
let (request_server, request_thread) = request::Server::listen(port).unwrap();
|
||||||
|
|
||||||
// start producing and consuming events
|
// start producing and consuming events
|
||||||
let event_producer = producer::start(produce_tx, client_manager.get_clients(), request_server);
|
let event_producer = match producer::start(produce_tx, client_manager.get_clients(), request_server) {
|
||||||
let event_consumer = consumer::start(consume_rx, client_manager.get_clients(), config.backend);
|
Err(e) => {
|
||||||
|
eprintln!("Could not start event producer: {e}");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(p) => Some(p),
|
||||||
|
};
|
||||||
|
let event_consumer = match consumer::start(consume_rx, client_manager.get_clients(), config.backend) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Could not start event consumer: {e}");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
Ok(p) => Some(p),
|
||||||
|
};
|
||||||
|
|
||||||
|
if event_consumer.is_none() && event_producer.is_none() {
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
// start sending and receiving events
|
// start sending and receiving events
|
||||||
let event_server = event::server::Server::new(port);
|
let event_server = match event::server::Server::new(port) {
|
||||||
let (receiver, sender) = event_server
|
Ok(s) => s,
|
||||||
.run(&mut client_manager, produce_rx, consume_tx)
|
Err(e) => {
|
||||||
.unwrap();
|
eprintln!("{e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (receiver, sender) = match event_server.run(&mut client_manager, produce_rx, consume_tx) {
|
||||||
|
Ok((r,s)) => (r,s),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
request_thread.join().unwrap();
|
request_thread.join().unwrap();
|
||||||
|
|
||||||
|
// stop receiving events and terminate event-consumer
|
||||||
receiver.join().unwrap();
|
receiver.join().unwrap();
|
||||||
sender.join().unwrap();
|
if let Some(thread) = event_consumer {
|
||||||
|
thread.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
event_producer.join().unwrap();
|
// stop producing events and terminate event-sender
|
||||||
event_consumer.join().unwrap();
|
if let Some(thread) = event_producer {
|
||||||
|
thread.join().unwrap();
|
||||||
|
}
|
||||||
|
sender.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::{thread::{JoinHandle, self}, sync::mpsc::SyncSender};
|
use std::{thread::{JoinHandle, self}, sync::mpsc::SyncSender, error::Error};
|
||||||
|
|
||||||
use crate::{client::{Client, ClientHandle}, event::Event, request::Server};
|
use crate::{client::{Client, ClientHandle}, event::Event, request::Server};
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ pub fn start(
|
|||||||
produce_tx: SyncSender<(Event, ClientHandle)>,
|
produce_tx: SyncSender<(Event, ClientHandle)>,
|
||||||
clients: Vec<Client>,
|
clients: Vec<Client>,
|
||||||
request_server: Server,
|
request_server: Server,
|
||||||
) -> JoinHandle<()> {
|
) -> Result<JoinHandle<()>, Box<dyn Error>> {
|
||||||
thread::Builder::new()
|
Ok(thread::Builder::new()
|
||||||
.name("event producer".into())
|
.name("event producer".into())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@@ -48,6 +48,5 @@ pub fn start(
|
|||||||
producer::wayland::run(produce_tx, request_server, clients);
|
producer::wayland::run(produce_tx, request_server, clients);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})?)
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,11 +60,13 @@ impl Server {
|
|||||||
|
|
||||||
pub fn listen(port: u16) -> Result<(Server, JoinHandle<()>), Box<dyn Error>> {
|
pub fn listen(port: u16) -> Result<(Server, JoinHandle<()>), Box<dyn Error>> {
|
||||||
let data: Arc<RwLock<HashMap<Request, MmapMut>>> = Arc::new(RwLock::new(HashMap::new()));
|
let data: Arc<RwLock<HashMap<Request, MmapMut>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
|
let listen_addr = SocketAddr::new("0.0.0.0".parse()?, port);
|
||||||
let server = Server { data };
|
let server = Server { data };
|
||||||
let server_copy = server.clone();
|
let server_copy = server.clone();
|
||||||
let thread = thread::spawn(move || {
|
let listen_socket = TcpListener::bind(listen_addr)?;
|
||||||
let listen_socket = TcpListener::bind(listen_addr).unwrap();
|
let thread = thread::Builder::new()
|
||||||
|
.name("tcp server".into())
|
||||||
|
.spawn(move || {
|
||||||
for stream in listen_socket.incoming() {
|
for stream in listen_socket.incoming() {
|
||||||
match stream {
|
match stream {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
@@ -75,7 +77,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})?;
|
||||||
Ok((server_copy, thread))
|
Ok((server_copy, thread))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user