I'm trying to create a reference to the current TCP connections. In this code, I'm trying to just print the current TCP connection's peer addresses in a loop and sleep for 10 seconds and the error I'm having is accessing the data between multiple threads.
I want to be able to manipulate a TCPStream from a different thread at any given point in time, to do things like shut down the TCPStream or get the peer address.
Can you please let me know what I'm doing wrong in a way that I can get a better understanding of how Arc and Mutex work?
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
let server = Server {
connected_clients: Arc::new(Mutex::new(Vec::new()))
};
thread::spawn(move || {
let listener = TcpListener::bind("127.0.0.1:25565").unwrap();
// For each new connection start a new thread
for stream in listener.incoming() {
let stream = Arc::new(Mutex::new(stream.unwrap()));
let client = Client {
stream: stream.clone()
};
let cc = server.connected_clients.clone();
cc.lock().unwrap().push(client);
thread::spawn(move || {
// TODO: Add client to the connected_clients Vec
let mut buffer = [0; 1024];
loop {
stream.lock().unwrap().read(&mut buffer).unwrap();
println!("{}", String::from_utf8(Vec::from(&buffer[..])).unwrap().trim_end_matches(char::from(0)));
}
});
}
});
loop {
thread::sleep(Duration::from_secs(10));
// let vec = server.lock().unwrap().connected_clients.lock().unwrap().iter();
for client in server.connected_clients.lock().unwrap().iter() {
println!("{:?}", client.stream.lock().unwrap().peer_addr().unwrap())
}
}
}
#[derive(Debug)]
struct Server {
connected_clients: Arc<Mutex<Vec<Client>>>,
}
#[derive(Debug)]
struct Client {
stream: Arc<Mutex<TcpStream>>
}
ERROR:
error[E0382]: borrow of moved value: `server.connected_clients`
--> src\main.rs:40:23
|
12 | thread::spawn(move || {
| ------- value moved into closure here
...
22 | let cc = server.connected_clients.clone();
| ------------------------ variable moved due to use in closure
...
40 | for client in server.connected_clients.lock().unwrap().iter() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
|
= note: move occurs because `server.connected_clients` has type `Arc<Mutex<Vec<Client>>>`, which does not implement the `Copy` trait
= note: borrow occurs due to deref coercion to `Mutex<Vec<Client>>`
CodePudding user response:
Just move the line let cc = server.connected_clients.clone();
before the first line thread::spawn(move || {
.
The move
keyword of the closure will now take ownership of cc
, then the original server.connected_clients
will stay available for the loop at the end of the program.
The idea behind Rc::clone()
or Arc::clone()
is exactly for the purpose of move
closures: instead of moving the original ref-counted pointer to the resource into the closure, we move its clone and the original ref-counted pointer to the resource is still available in its original context.