Home > OS >  Reference to a TCPListener and TcpStream from one thread to a Struct in another thread
Reference to a TCPListener and TcpStream from one thread to a Struct in another thread

Time:08-11

The current issue that I am having is creating a way to reference the TCPListener from a thread into the Struct of the main thread. The end goal of what I am trying to accomplish is a way to reference the server's TCP connections from the Server struct.

Here is the code:

use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

fn main() {
    let server = Server::start("127.0.0.1:25565".to_string());
    loop {
        for client in server.connected_clients {
            println!("{:?}", client.stream.peer_addr().unwrap())
        }
    }
}

#[derive(Debug)]
struct Server {
    listener: Arc<Mutex<TcpListener>>,
    connected_clients: Vec<Client>,
}

impl Server {
    pub fn start(address: String) -> Server {
        let listener = TcpListener::bind(address).unwrap();

        let (tx, rx) = mpsc::channel();
        let listener = Arc::new(Mutex::new(listener));

        let server = Server {
            listener: listener,
            connected_clients: Vec::new()
        };

        tx.send(&server.listener).unwrap();

        thread::spawn(|| {
            let listener = rx.recv().unwrap();
            // For each new connection start a new thread
            for stream in listener.lock().unwrap().incoming() {
                let mut stream = stream.unwrap();
                thread::spawn(move || {
                    // TODO: Add client to the connected_clients Vec
                    let mut buffer = [0; 1024];
                    loop {
                        stream.read(&mut buffer).unwrap();
                        println!("{}", String::from_utf8(Vec::from(&buffer[..])).unwrap().trim_end_matches(char::from(0)));
                    }
                });
            }
        });

        server
    }
}

#[derive(Debug)]
struct Client {
    id: usize,
    stream: TcpStream,
}

ERROR: std::sync::mpsc::Receiver<&Arc<Mutex>>` cannot be shared between threads safely

CodePudding user response:

I'm uncertain where you got the idea from to use channel to send an object to a thread you just spawned, but it's definitely incorrect in this case.

Just use a move || closure instead to move outer variables into it.

With that in mind, this code compiles: (although I have no way to test if it works)

use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;

fn main() {
    let server = Server::start("127.0.0.1:25565".to_string());
    loop {
        for client in &server.connected_clients {
            println!("{:?}", client.stream.peer_addr().unwrap())
        }
    }
}

#[derive(Debug)]
struct Server {
    listener: Arc<Mutex<TcpListener>>,
    connected_clients: Vec<Client>,
}

impl Server {
    pub fn start(address: String) -> Server {
        let server = Server {
            listener: Arc::new(Mutex::new(TcpListener::bind(address).unwrap())),
            connected_clients: Vec::new(),
        };

        let listener = server.listener.clone();
        thread::spawn(move || {
            // For each new connection start a new thread
            for stream in listener.lock().unwrap().incoming() {
                let mut stream = stream.unwrap();
                thread::spawn(move || {
                    // TODO: Add client to the connected_clients Vec
                    let mut buffer = [0; 1024];
                    loop {
                        stream.read(&mut buffer).unwrap();
                        println!(
                            "{}",
                            String::from_utf8(Vec::from(&buffer[..]))
                                .unwrap()
                                .trim_end_matches(char::from(0))
                        );
                    }
                });
            }
        });

        server
    }
}

#[derive(Debug)]
struct Client {
    id: usize,
    stream: TcpStream,
}

Technically, while being complete overkill in this case, your channel approach would work as well, but then you have to send an actual instance of the object instead of a reference. And you still need to use a move || closure to move the rx object into it.

use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{mpsc, Arc, Mutex};
use std::thread;

fn main() {
    let server = Server::start("127.0.0.1:25565".to_string());
    loop {
        for client in &server.connected_clients {
            println!("{:?}", client.stream.peer_addr().unwrap())
        }
    }
}

#[derive(Debug)]
struct Server {
    listener: Arc<Mutex<TcpListener>>,
    connected_clients: Vec<Client>,
}

impl Server {
    pub fn start(address: String) -> Server {
        let listener = TcpListener::bind(address).unwrap();

        let (tx, rx) = mpsc::channel();
        let listener = Arc::new(Mutex::new(listener));

        let server = Server {
            listener: listener,
            connected_clients: Vec::new(),
        };

        tx.send(server.listener.clone()).unwrap();

        thread::spawn(move || {
            let listener = rx.recv().unwrap();
            // For each new connection start a new thread
            for stream in listener.lock().unwrap().incoming() {
                let mut stream = stream.unwrap();
                thread::spawn(move || {
                    // TODO: Add client to the connected_clients Vec
                    let mut buffer = [0; 1024];
                    loop {
                        stream.read(&mut buffer).unwrap();
                        println!(
                            "{}",
                            String::from_utf8(Vec::from(&buffer[..]))
                                .unwrap()
                                .trim_end_matches(char::from(0))
                        );
                    }
                });
            }
        });

        server
    }
}

#[derive(Debug)]
struct Client {
    id: usize,
    stream: TcpStream,
}

One last remark:

I question the usefulness of the entire construct, though. As soon as your thread is inside the for loop, your listener is continuously locked, meaning, whoever tries to actually access it through your Server object will simply deadlock.

  • Related