Can't connect to server from any device other than local machine #6264
-
I have a simple-ish WebSocket server which is using Tokio + Tokio-Tungstenite, and I want to connect to this server via a WebSocket in JS from another device on the same network. It simply refuses to connect. I have tried with firewall enabled/disabled, both WiFi and Hotspot, a great range of different hosts, including 0.0.0.0 (the apparent correct host for accepting connections from any IP) and I still cannot get a connection from my other device. It all works perfectly from the same machine that the server was started on, and I have no idea what is wrong. It seems like everywhere I look everyone has fixed this simply by changing their IP from 127.0.0.1 to 0.0.0.0 or disabling their firewall. When I attempt to connect from another device, it either gets timed out, or simply throws Connection refused. I have never even once got it to recognise the server exists, and the server never gets any sort of connection incoming ever when connecting from the other device, even though they are definitely on the same network and nothing is blocking them, and I can connect perfectly from my NodeJS server (as mentioned later on). My implementation for this server is below: // main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use hostname::get;
use lazy_static::lazy_static;
use tokio::sync::Mutex;
mod socket;
use socket::{create_server, get_host_and_port};
lazy_static! {
static ref SERVER_URI: Mutex<Option<String>> = Mutex::new(None);
}
#[tauri::command]
async fn get_server_uri() -> String {
let mut server_uri = SERVER_URI.lock().await;
if let Some(uri) = &*server_uri {
return uri.clone();
}
let start_port: u16 = 5253;
let (host, port) = get_host_and_port(start_port).await;
let host_os_str = get().expect("Failed to get hostname.");
let host_str = host_os_str
.to_str()
.expect("Failed to convert hostname to &str");
let uri = format!("ws://{}:{}", host_str, port);
*server_uri = Some(uri.clone());
create_server(host, port).await;
uri
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![get_server_uri])
.run(tauri::generate_context!())
.expect("error while running tauri application");
} // socket.rs
use std::{
collections::HashMap,
io::ErrorKind,
net::{IpAddr, SocketAddr, TcpListener as Std__TcpListener},
str::FromStr,
sync::{Arc, Mutex},
};
use futures_channel::mpsc::{unbounded, UnboundedSender};
use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::{accept_async, tungstenite::protocol::Message};
use serde_json::{Result, Value};
type Tx = SenderWithAddr<Message>;
type Clients = Arc<Mutex<HashMap<SocketAddr, Tx>>>;
struct SenderWithAddr<T> {
sender: UnboundedSender<T>,
addr: SocketAddr,
}
impl<T> SenderWithAddr<T> {
fn new(sender: UnboundedSender<T>, addr: SocketAddr) -> Self {
SenderWithAddr { sender, addr }
}
}
pub async fn create_server(host: IpAddr, port: u16) {
let listener = TcpListener::bind((host, port)).await.unwrap();
println!("Server listening on {}", port);
let clients = Clients::new(Mutex::new(HashMap::new()));
tokio::spawn(async move {
loop {
let res = listener.accept().await;
match res {
Ok((stream, addr)) => {
tokio::spawn(handle_connection(clients.clone(), stream, addr));
}
Err(e) => {
eprintln!("Failed to accept connection: {}", e);
}
}
}
});
}
async fn handle_connection(clients: Clients, stream: TcpStream, addr: SocketAddr) {
println!("Incoming TCP connection from: {}", addr);
let ws_stream = accept_async(stream)
.await
.expect("Error during the websocket handshake occurred");
println!("WebSocket connection established: {}", addr);
let (tx, rx) = unbounded();
let tx = SenderWithAddr::new(tx, addr);
clients.lock().unwrap().insert(addr, tx);
let (outgoing, incoming) = ws_stream.split();
let broadcast_incoming = incoming.try_for_each(|msg| {
let connections = clients.lock().unwrap();
let msg_string = msg
.into_text()
.expect("Failed to convert message into string!");
let msg_json: Result<HashMap<String, Value>> = serde_json::from_str(&msg_string);
let data = match msg_json {
Ok(res) => res,
Err(_) => {
let mut res = HashMap::new();
res.insert("content".to_string(), Value::String(msg_string));
res
}
};
// Get all of the connections which aren't this connection
let broadcast_recipients = connections
.iter()
.filter(|(peer_addr, _)| peer_addr != &&addr)
.map(|(_, ws_sink)| ws_sink);
let content = match data.get("content") {
Some(content) => content
.as_str()
.expect("Failed to convert content to string."),
None => "null",
};
println!("Received a message from {}: {}", addr, content);
for recp in broadcast_recipients {
println!("Sending the message to {}", recp.addr);
recp.sender
.unbounded_send(Message::Text(content.to_string()))
.unwrap();
}
future::ok(())
});
let receive_from_others = rx.map(Ok).forward(outgoing);
pin_mut!(broadcast_incoming, receive_from_others);
future::select(broadcast_incoming, receive_from_others).await;
println!("{} disconnected", &addr);
clients.lock().unwrap().remove(&addr);
}
pub async fn get_host_and_port(start_port: u16) -> (IpAddr, u16) {
let mut port = start_port;
let host = IpAddr::from_str("0.0.0.0").unwrap();
loop {
match Std__TcpListener::bind((host, port)) {
Ok(listener) => {
drop(listener);
break (host, port);
}
Err(ref err) if err.kind() == ErrorKind::AddrInUse => {
port += 1;
}
Err(err) => {
panic!("Error binding to port: {}", err);
}
}
}
} It is also part of a Tauri app, so there are some irrelevant sections, however the code is mostly relevant. I also have an equivalent-like implementation in NodeJS, which works perfectly, and I can connect on my other client via the same URL, including the same port, perfectly. This implementation is shown below: import { WebSocket, WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: parseInt(process.argv[2]) || 4000 });
wss.on("connection", (ws: WebSocket) => {
console.log("A socket connected");
ws.on("message", (message: Buffer) => {
try {
const data = JSON.parse(message.toString());
for (let client of wss.clients) {
// ...
}
} catch {}
});
}); I'm not quite sure if this is relevant, but here is a video of the connection working, when the WebSockets are both connected from the host/local machine. This is probably reproducible by:
tauri = { version = '1.5', features = ['shell-open'] }
serde = { version = '1.0', features = ['derive'] }
tokio = { version = '1.35.1', features = ['full'] }
tokio-tungstenite = '0.21.0'
serde_json = '1.0'
lazy_static = '1.4.0'
futures-channel = '0.3.30'
futures-util = '0.3.30'
hostname = '0.3.1'
import { invoke } from '@tauri-apps/api/tauri';
invoke('get_server_uri').then(console.log).catch(console.log);
new WebSocket('<uri returned by the invokation>') on both the same machine where the server was created and a seperate machine on the same network. I hope I provided enough information, |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
You have a lot of Rust code that complicates matters. Did you try creating a |
Beta Was this translation helpful? Give feedback.
Okay, it seems that after restarting both devices, it now works? The main code itself also works now for some reason but only when running the production version 😭
Thank you, I don't know what you did but it's magically working now what the heck aaaaa