Rust server must save the updated value of variable

Viewed 84

I need to realize TCP server, that stores a variable M. At the beginning, M = 1. But, when the conntection set between server and client, and client sends another value of variable N, server must do next: M = M * N. And return that value to client. And server must save that new value of variable M! And, when next new client set connection, it will work with new value of variable. Example:

  1. Server: M = 1; Client: N = 5; Server: M = 5;
  2. Server: A = 5; Client: N = 8; Server: M = 40;

This is my code of my server. And it works in that way:(not saving new value) Example:

  1. Server: M = 1; Client: N = 5; Server: M = 5;
  2. Server: A = 1; Client: N = 8; Server: M = 8;

Maybe should I do a global variable? Or something about it... Give me some advice. Thank you.

fn handle_client(mut stream: TcpStream, a:&mut i32) {
    let mut data = [0 as u8; 30]; // using 30 byte buffer
    
    while match stream.read(&mut data) {
    
        Ok(size) => {
           if size>0{
           
           let temp = str::from_utf8(&data[0..size]).unwrap().to_string();
                        
           let temp: i32  = temp.trim().parse().unwrap();                  
           
            *a = *a * temp;
                  
            let st = a.to_string();
            // String в u8
            let data = st.as_bytes();
            

            stream.write(&data).unwrap();
           }
            true
        },
        Err(_) => {
            println!("An error occurred, terminating connection with {}", stream.peer_addr().unwrap());
            stream.shutdown(Shutdown::Both).unwrap();
            false
        }
    } {}
}


fn main() {
    let listener = TcpListener::bind("0.0.0.0:7956").unwrap();
    
   let mut a: i32 = 1;
    // accept connections and process them, spawning a new thread for each one
    println!("Server listening on port 7956");
    println!("A = 1");
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                println!("New connection: {}", stream.peer_addr().unwrap());
                thread::spawn(move|| {
                    // connection succeeded
                    handle_client(stream, &mut a)
                });
            }
            Err(e) => {
                println!("Error: {}", e);
                /* connection failed */
            }
        }
    }
    // close the socket server
    drop(listener);
} 

1 Answers

Why doesn't your code work?

Because you move a into the closure, thus copying it. In order to se that, you just have to ask: What do you need to move into the closure, such that &mut a is of type &mut i32?
Exactly, a "raw" i32. How does the compiler do that? By copying it. That means changes to the variable "inside" the closure will not be reflected "outside".

Well, then how to fix it?

Let's start from the beginning: If you simply don't borrow in the call, you will move a mutable reference into the closure - that doesn't work though, due to two reasons: Firstly, it is obviously not thread safe, and secondly, the lifetime of the borrow cant even be guaranteed to be long enough ('static).

How do we work around that: For the first part, the thread-safety, we could use a Mutex. That's overkill for a simple integer though. We use an atomic. For the second part, we could use an Arc - I again went with the simpler option, make it a static ("global") variable.
Edit: In the comments it was raised to my attention, that maybe my views of a "simple" solution to this problem are a little skewed, this is a link to the chapter on thread-safety in the book, which uses an Arc<Mutex<_>> to count to ten in parallel. Every Rust programmer should have read that section (and the entire book!).

So here is your code, with only the relevant parts rewritten:

use std::sync::atomic;

fn handle_client(mut stream: TcpStream) {
    let mut data = [0 as u8; 30]; // using 30 byte buffer

    while match stream.read(&mut data) {
        Ok(size) => {
            if size > 0 {
                let temp = std::str::from_utf8(&data[0..size]).unwrap().to_string();

                let temp: i32 = temp.trim().parse().unwrap();

                let prev_a_val = loop {
                    let a_acquired = a.load(atomic::Ordering::Acquire);
                    match a.compare_exchange_weak(
                        a_acquired,
                        a_acquired * temp,
                        atomic::Ordering::AcqRel,
                        atomic::Ordering::Acquire,
                    ) {
                        Ok(value) => break value,
                        Err(_) => {}
                    }
                };

                let st = (prev_a_val * temp).to_string();
                // String в u8
                let data = st.as_bytes();

                stream.write(&data).unwrap();
            }
            true
        }
        Err(_) => {
            println!(
                "An error occurred, terminating connection with {}",
                stream.peer_addr().unwrap()
            );
            stream.shutdown(Shutdown::Both).unwrap();
            false
        }
    } {}
}

static a: atomic::AtomicI32 = atomic::AtomicI32::new(1);

fn main() {
    let listener = TcpListener::bind("0.0.0.0:7956").unwrap();

    // accept connections and process them, spawning a new thread for each one
    println!("Server listening on port 7956");
    println!("A = 1");
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                println!("New connection: {}", stream.peer_addr().unwrap());
                thread::spawn(|| {
                    // connection succeeded
                    handle_client(stream)
                });
            }
            Err(e) => {
                println!("Error: {}", e);
                /* connection failed */
            }
        }
    }
}
Related