Sandip
Sandip

Reputation: 206

multiple websocket servers in rust

I referred this and also tried tungstenite library. But I was able to run only one server at a time, it captured whole thread.

I tried running multiple servers on different thread but that never listen anything and just exit the program.

Is there anyway that I can run multiple WebSocket servers on different ports, and create, destroy a server in runtime?

Edit: If I run a server on main thread and another one on other thread, it works, looks like I'd have to keep main thread busy somehow.. but is there any better way?

here's some example code:

it uses:

use std::net::TcpListener;
use std::thread::spawn;
use tungstenite::accept;

this is the normal code that blocks the main thread

let server = TcpListener::bind("127.0.0.1:9002").expect("err: ");
        for stream in server.incoming() {
            spawn(move || {
                let mut websocket = accept(stream.unwrap()).unwrap();
                loop {
                    let msg = websocket.read_message().unwrap();

                    println!("{}", msg);

                    // We do not want to send back ping/pong messages.
                    if msg.is_binary() || msg.is_text() {
                        websocket.write_message(msg).unwrap();
                    }
                }
            });
        }

here's the code with thread:

spawn(|| {
        let server = TcpListener::bind("127.0.0.1:9001").expect("err: ");
        for stream in server.incoming() {
            spawn(move || {
                let mut websocket = accept(stream.unwrap()).unwrap();
                loop {
                    let msg = websocket.read_message().unwrap();

                    println!("{}", msg);

                    // We do not want to send back ping/pong messages.
                    if msg.is_binary() || msg.is_text() {
                        websocket.write_message(msg).unwrap();
                    }
                }
            });
        }
    });

but the above code needs the main thread to run somehow, I'm indeed able to run multiple servers on different threads but need something to occupy main thread.

Upvotes: 1

Views: 1255

Answers (2)

justinas
justinas

Reputation: 6867

Rust programs terminate when the end of main() is reached. What you need to do is wait until your secondary threads have finished.

std::thread::spawn returns a JoinHandle, which has a join method which does exactly that - it waits (blocks) until the thread that the handle refers to finishes, and returns an error if the thread panicked.

So, to keep your program alive as long as any threads are running, you need to collect all of these handles, and join() them one by one. Unlike a busy-loop, this will not waste CPU resources unnecessarily.

use std::net::TcpListener;
use std::thread::spawn;
use tungstenite::accept;

fn main() {
    let mut handles = vec![];

    // Spawn 3 identical servers on ports 9001, 9002, 9003
    for i in 1..=3 {
        let handle = spawn(move || {
            let server = TcpListener::bind(("127.0.0.1", 9000 + i)).expect("err: ");
            for stream in server.incoming() {
                spawn(move || {
                    let mut websocket = accept(stream.unwrap()).unwrap();
                    loop {
                        let msg = websocket.read_message().unwrap();

                        println!("{}", msg);

                        // We do not want to send back ping/pong messages.
                        if msg.is_binary() || msg.is_text() {
                            websocket.write_message(msg).unwrap();
                        }
                    }
                });
            }
        });
        handles.push(handle);
    }

    // Wait for each thread to finish before exiting
    for handle in handles {
        if let Err(e) = handle.join() {
            eprintln!("{:?}", e)
        }
    }
}

Upvotes: 1

rodrigo
rodrigo

Reputation: 98496

When you do all the work in a thread (or threads) and the main thread has nothing to do, usually it is set to wait (join) that thread.

This has the additional advantage that if your secondary thread finishes or panics, then your program will also finish. Or you can wrap the whole create-thread/join-thread in a loop and make it more resilient:

fn main() {
    loop {
        let th = std::thread::spawn(|| {
            // Do the real work here
            std::thread::sleep(std::time::Duration::from_secs(1));
            panic!("oh!");
        });
        if let Err(e) = th.join() {
            eprintln!("Thread panic: {:?}", e)
        }
    }
}

Link to playground, I've changed to the loop into a for _ in ..3 because playgrond does not like infinite loops.

Upvotes: 0

Related Questions