Unable to read tokio TcpStream with Arc and RwLock


use std::ops::Add;
use std::sync::Arc;
use tokio::io::AsyncReadExt;
use tokio::sync::RwLock;
use tokio::net::{TcpListener, TcpStream};
use crate::helper::filter::extract_protocol_info;

pub async fn  client_run(config:Config) {
    let addr = "127.0.0.1".to_string();
    let local_add = addr.add(":").add(&*config.local_port);
    let listener = TcpListener::bind(&local_add).await.unwrap();
    log::info!("client start at {} listening  package coming from application",&local_add);
    loop {
        match listener.accept().await {
            Ok((socket, socket_add)) => {
                let socket = Arc::new(RwLock::new(socket));
                let task = tokio::spawn(async move {
                    log::info!("new application connected at : {}", socket_add);
                    select_protocal(Arc::clone(&socket)).await;
                });

                task.await.expect("TODO: panic message");
            }
            Err(err) => {
                log::error!("Error accepting connection: {:?}", err);
            }
        }
    }
}
async fn select_protocal (stream: Arc<RwLock<TcpStream>>) {
    println!("{:?}",stream);
    let mut buffer :Vec<u8>= Vec::new();
    let mut read_lock = stream.read().await;
    let read = read_lock.read_to_end(&mut buffer);
    println!("{:?}",read);
    println!("{:?}",buffer);
}

this line caused error

let read = read_lock.read_to_end(&mut buffer);

got this error


error[E0596]: cannot borrow data in dereference of `tokio::sync::RwLockReadGuard<'_, tokio::net::TcpStream>` as mutable
  --> src\runner\client_run.rs:57:16
   |
57 |     let read = read_lock.read_to_end(&mut buffer);
   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `tokio::sync::RwLockReadGuard<'_, tokio::net::TcpStream>`

The read_to_end method expects a mutable reference:

read_to_end(&mut self, buf: &mut Vec<u8>)

And you are using a read lock. Use the write lock instead.

1 Like

read_to_end requires mutable access to the TcpStream (notice the &mut self in the signature). Therefore, you need to call write() on the RwLock (RwLock::write()).

Edit: The requirement for the mutable access comes from the AsyncRead trait (which AsyncReadExt is an extension trait of). But you don't necessarily need mutable access to the TcpStream to read from it. If you use the inherent methods of TcpStream like try_read, you don't need a lock around the TcpStream. You would need to build your own read_to_end however.

1 Like

thansk . pass compiled . i change it to write , after this

println!("{:?}",buffer);

output empty . looks . if i change

read_lock.read_to_end(&mut buffer);

to

read_lock.read_to_end(&mut buffer)..await;

whole thread stuck in

Yes, you need to .await the return value of read_to_end as it is a Future that does nothing unless you .await it. The compiler output should contain a warning saying exactly this.

read_to_end "reads all bytes until EOF in this source, placing them into buf". If you're not receiving EOF on the stream, then read_to_end().await will never complete. Looking at your code again, you probably only want to read from the stream until you know the protocol (identifier?) that is transmitted.

On a more general note. If you're just starting with Rust, I would heavily recommend to wait with asynchronous programming. It is currently one of the less polished parts of the language and really damn difficult. Using synchronous code with threads will likely be far easier.

1 Like

Putting a TcpStream inside a mutex or rwlock is almost always a mistake. Instead, you should probably do one of these:

  • Just remove the Arc and RwLock if it does not need to be shared.
  • To share it, use the actor pattern, and share actor handles instead of sharing the TcpStream itself.
2 Likes

thanks for your kind answer :pray:

1 Like

let me try and thank you