Unable to keep the TcpStream alive for a single connection

Hello

I am trying to make a TCPServer and a TCPClient in the same program in Rust, using the tokio runtime, I am trying to understand the concepts involved here, this is more of a learning project.

use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}};

#[tokio::main]
async fn main() 
{
    let listener = TcpListener::bind("0.0.0.0:2345").await.unwrap();

     tokio::spawn( async move {
        loop {
            println!("Running");
            match listener.accept().await {
                Ok(mut w) => {
                    let mut buffer = [0u8; 1024];
                    w.0.read(&mut buffer).await.unwrap();
                    println!("{}", String::from_utf8_lossy(buffer.as_ref()));
                }
                Err(e) => {
                    println!("This is e {}", e);
                }
            }
        }
    });

     
    let mut con = TcpStream::connect("0.0.0.0:2345").await.unwrap();
    tokio::spawn(async move {
        loop {
            con.write_all("Hello World".as_bytes()).await.unwrap();
            con.flush().await.unwrap();
        }
    });

    loop{};
}

I have tried set_linger() and set_delay to check if it will work, I have found no success with those. I read that tokio closes the port when it reads the stream, with my understanding and search, I have tried ways to keep it open.

I am getting errors such as BrokenPipe and ConnectionReset. What should I do? Thank you in advance.

I haven’t worked with tokio’s TCP library, but it’s probably been designed to close the connection when w is dropped, and therefore no longer accessible. In your current code, this happens at the end of the match statement— You probably want to spawn a new task for each connection that listener.accept() gives you that reads in a loop until the connection is closed from the other side.

1 Like

The TcpStream is closed when you run the destructor of w. To keep it open, avoid running the destructor.

1 Like

Hello, I tried it just now, but it did not work.

 loop {
            println!("Running");
            match listener.accept().await {
                Ok(mut w) => {
                        tokio::spawn(async move {
                        let mut buffer = [0u8; 1024];
                        w.0.read(&mut buffer).await.unwrap();
                        println!("{}", String::from_utf8_lossy(buffer.as_ref()));

                    });
                }
                Err(e) => {
                    println!("This is e {}", e);
                }
            }
        }

Your connection still goes out of scope after your read.

Try something like this (untested):

tokio::spawn(async move {
    let mut buffer = [0u8; 1024];
    let mut cursor = 0;
    loop {
        let n = w.0.read(&mut buffer[cursor..]).await.unwrap();
        if n == 0 {break};
        cursor += n;
    }
    println!("{}", String::from_utf8_lossy(&buffer[0..cursor]);
});

(Note that this won’t print until the sender has either closed the connection or sent 1k bytes, so you’ll need to drop the writing connection once you’re finished putting data into it)

Hello Alice, first of all thank you, your comment pointed out a flaw in my understanding

let listener = TcpListener::bind("0.0.0.0:2345").await.unwrap();

     tokio::spawn( async move {
        loop {
            println!("Running");
            match listener.accept().await {
                Ok(mut w) => {
                        tokio::spawn(async move {
                            loop {
                                let mut buffer = [0u8; 1024];
                                w.0.read(&mut buffer).await.unwrap();
                                println!("{}", String::from_utf8_lossy(buffer.as_ref()));
                            }
                    });
                }
                Err(e) => {
                    println!("This is e {}", e);
                }
            }
        }
    });

     
    let mut con = TcpStream::connect("0.0.0.0:2345").await.unwrap();
    tokio::spawn(async move {
            con.write("Hello World".as_bytes()).await.unwrap();
            con.flush().await.unwrap();
    });

    loop{};

I did this, I have tried 2 variations of this, one is the one above, in the second variation I am trying have just looped the hello world as well, no connection lost thing happened.

Thanks a lot for showing me the way. I will try this just now and let you know.

So here is what I did, it is working and no connection reset is coming now.

I have one more question

use std::{thread, time::Duration};

use tokio::{io::{self, AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}};

#[tokio::main]
async fn main() 
{
    let listener = TcpListener::bind("0.0.0.0:2345").await.unwrap();

     tokio::spawn( async move {
        loop {
            println!("Running");
            match listener.accept().await {
                Ok(mut w) => {
                        tokio::spawn(async move {
                            loop {
                                let mut buffer = [0u8; 1024];
                                let n = w.0.read(&mut buffer).await.unwrap();

                                if n == 0 {
                                    println!("Zero was found");
                                    break; 
                                } 
                                println!("{}", String::from_utf8_lossy(buffer.as_ref()));
                            }
                    });
                }
                Err(e) => {
                    println!("This is e {}", e);
                }
            }
        }
    });

     
    let mut con = TcpStream::connect("0.0.0.0:2345").await.unwrap();
    tokio::spawn(async move {
        con.write("Hello World".as_bytes()).await.unwrap();
        con.flush().await.unwrap();
        thread::sleep(Duration::from_secs(1));
        con.write("Bye World".as_bytes()).await.unwrap();
        con.write("Bye World".as_bytes()).await.unwrap();
        con.write("Bye World".as_bytes()).await.unwrap();
        con.write("Bye World".as_bytes()).await.unwrap();
    });

    loop{};
}

Once it finds zero, and it will call break, if I try to do what I was doing earlier, I will get the same error, right?

The read function will only return zero if the person writing to you has closed the connection (in their write direction) from their end.

2 Likes

It can also return 0 if a 0-length buffer is passed in. This can happen in my example code, but not the version that @romani ended up with.

1 Like

Thank you very much Alice!

Thank you very much @2e71828 !