Как хранить TcpStream внутри HashMap? - PullRequest
0 голосов
/ 14 февраля 2020

Я новичок в Rust и пытаюсь создать структуру сервера, которая прослушивает адрес и запускает соединение через сокет TCP. Проблема в том, что я хочу сохранить клиентское соединение внутри карты ha sh, чтобы я мог использовать ее позже ..

Я попытался написать это:

use std::collections::HashMap;
use std::net::TcpListener;
use std::net::TcpStream;
use std::sync::{Arc, RwLock};
use std::thread;

#[derive(Clone, Debug)]
pub struct Server {
    id: Arc<RwLock<u32>>,
    connections: Arc<RwLock<HashMap<u32, TcpStream>>>,
    url: String,
}

impl Server {
    pub fn new(url: String) -> Server {
        let server = Server {
            id: Arc::new(RwLock::new(0)),
            connections: Arc::new(RwLock::new(HashMap::new())),
            url,
        };

        server
    }

    pub fn start(&self) {
        thread::spawn(move || {
            let mut listener =
                TcpListener::bind(self.clone().url).expect("Could not start the server");

            println!("Server started succesfully");

            for stream in listener.incoming() {
                match stream {
                    Ok(stream) => self.on_client_connect(stream),
                    Err(error) => eprintln!("Error when tried to use stream"),
                }
            }
        });
    }

    fn on_client_connect(&mut self, stream: TcpStream) {
        let id = self.id.read().unwrap();
        self.connections.read().unwrap().insert(id, stream);
        let id = self.id.write().unwrap();
        *id += 1;
    }
}

Но, конечно, это не работает .. Есть 2 вещи, которые я не понимаю, во-первых, как передать поток в мою функцию, а затем сохранить в моих картах соединений ha sh, чтобы я мог использовать его позже и как использовать мой идентификатор внутри моей функции on_client_connect.

Ответы [ 2 ]

2 голосов
/ 14 февраля 2020

Вам необходимо клонировать за пределами thread::spawn и переместить клонированный экземпляр в область видимости потока.

Также on_client_connect не нужно &mut self, так как поля id и connections уже заполнены внутри защищено RwLock.

use std::net::TcpListener;
use std::net::TcpStream;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;

#[derive(Clone, Debug)]
pub struct Server {
    id: Arc<RwLock<u32>>,
    connections: Arc<RwLock<HashMap<u32, TcpStream>>>,
    url: String,
}

impl Server {
    pub fn new(url: String) -> Server {
        let server = Server {
            id: Arc::new(RwLock::new(0)),
            connections: Arc::new(RwLock::new(HashMap::new())),
            url,
        };

        server
    }

    pub fn start(&self) {
        let me = self.clone(); // Clone it outside
        thread::spawn(move || {
            let mut listener = TcpListener::bind(&me.url).expect("Could not start the server");

            println!("Server started succesfully");

            for stream in listener.incoming() {
                match stream {
                    Ok(stream) => me.on_client_connect(stream),
                    Err(error) => eprintln!("Error when tried to use stream"),
                }
            }
        });
    }

    fn on_client_connect(&self, stream: TcpStream) { // `&mut self` not needed as the id, connection are inside the lock
        let mut id = self.id.write().unwrap();
        self.connections.write().unwrap().insert(*id, stream);
        *id += 1;
    }
}

детская площадка

1 голос
/ 14 февраля 2020

В этом коде немало проблем, требующих незначительных исправлений.

Первое, с чем я столкнулся, было использование self в закрытии thread::spawn. Для потока :: spawn необходимо, чтобы его аргумент имел время жизни c, но мы не можем гарантировать, что объект Server будет жить так долго.

Я решил это путем клонирования объекта Server и перемещения его в замыкание. Это нормально, так как все его данные уже отстали Arc с.

Следующая проблема состояла в том, что self.connections.read().unwrap().insert(*id, stream); необходимо получить блокировку write, а не read.

Наконец id+=1 необходимо разыменовать id.

Как только они были исправлены, кажется, что сохранение TcpStream не является проблемой. (По крайней мере, с использованием ночных). Я подумал, что мне нужно запаковать TcpStream - но, похоже, все в порядке.

Вы можете видеть, что он компилирует на игровой площадке

use std::collections::HashMap;
use std::net::TcpListener;
use std::net::TcpStream;
use std::sync::{Arc, RwLock};
use std::thread;

#[derive(Clone, Debug)]
pub struct Server {
    id: Arc<RwLock<u32>>,
    connections: Arc<RwLock<HashMap<u32, TcpStream>>>,
    url: String,
}

impl Server {
    pub fn new(url: String) -> Server {
        let server = Server {
            id: Arc::new(RwLock::new(0)),
            connections: Arc::new(RwLock::new(HashMap::new())),
            url,
        };

        server
    }

    pub fn start(&self) {
        let mut self_clone = self.clone();
        thread::spawn(move || {
            let mut listener =
                TcpListener::bind(&self_clone.url).expect("Could not start the server");

            println!("Server started succesfully");

            for stream in listener.incoming() {
                match stream {
                    Ok(stream) => self_clone.on_client_connect(stream),
                    Err(error) => eprintln!("Error when tried to use stream"),
                }
            }
        });
    }

    fn on_client_connect(&mut self, stream: TcpStream) {
        let id = self.id.read().unwrap();
        self.connections.write().unwrap().insert(*id, stream);
        let mut id = self.id.write().unwrap();
        *id += 1;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...