How do I write an asynchronous port checker?

The std::net::TcpStream has a connect_timeout function, but now that I'm limited to strictly non-blocking functions, how would I do this?

Also, I'm new to async code, I just want to clarify, do I need to block if I need something to finish before another task is processed?

This re-solvability check is checking if a given port is open before it runs tests on this port, and I need it to run before running the tests.

My current code looks like this, it does not work correctly (It seems to get stuck and never return, or if it does work, I may need to ask for some code review; my system is not working correctly...):

async fn resolvable(ip: String, port: u32, timeout_seconds: u64) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let socket: SocketAddr = format!("{}:{}", ip, port).parse()?;
    let stream = TcpStream::connect(socket);
    if let Err(_) = tokio::time::timeout(Duration::from_secs(timeout_seconds), stream).await {
        Err("Port or IP address is not reachable.".into())
    } else {
        Ok(())
    }
}

Hi, I can see a few problems here:

  1. You marked resolvable as async, but the current return type doesn't reflect this requirement. Asynchronous functions must return futures.
  2. You mixed synchronous, blocking code with asynchronous code. You could use TcpStream::connect method from the async-std crate (this crate provides async versions of nearly all std methods that would block) and .await the result.
  3. You need an asynchronous runtime to call your resolvable function.

However, I would recommend you read the whole Rust Aync Book, as it explains all the twists, requirements and principles of asynchronous Rust programming to make a project like this. :wink:

e: doesn't have much time right now, but this example works for me. I also read over that you're using tokio, so you have to translate the async_std bits in my code to the corresponding tokio alternatives.

use async_std::{io, net};
use std::net::SocketAddr;

async fn resolvable(
    ip: String,
    port: u32,
    timeout_seconds: u64,
) -> Result<net::TcpStream, Box<dyn std::error::Error + Send + Sync>> {
    let socket: SocketAddr = format!("{}:{}", ip, port).parse()?;

    io::timeout(
        std::time::Duration::from_seconds(timeout_seconds),
        net::TcpStream::connect(socket),
    )
    .await
    .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync>)
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let res = resolvable("127.0.0.1".into(), 4000, 1).await;

    match res {
        Ok(_) => println!("Sucessfully connected"),
        Err(err) => println!("Error: timed out: {}", err),
    }

    Ok(())
}

1 Like

You're thinking of Typescript, I think.

2 Likes

Thanks for the help! I think I'm getting somewhere now. The code provided had a few issues, but now, when I try running this, I get no prints at all! This is really confusing me because I'm pretty sure I'm following everything correctly.

async fn resolvable(ip: String, port: u32, timeout_seconds: u64) -> Result<tokio::net::TcpStream, Box<dyn std::error::Error + Send + Sync>> {
    tokio::time::timeout(std::time::Duration::from_secs(timeout_seconds), tokio::net::TcpStream::connect(format!("{}:{}", ip, port)))
        .await?
        .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync>)
}

#[tokio::main]
async fn main() {
    let mut jhs = Vec::new();
    for port in 0..101 {
        let jh = tokio::spawn(async move {
            match resolvable("127.0.0.1".into(), port, 5).await {
                Ok(_) => println!("Port {} open!", port),
                Err(err) => println!("Error: timed out: {} Port {} closed.", err, port),
            }
        });
        jhs.push(jh);
    }
    for jh in jhs {
        jh.await;
    }
}

Am I overlooking anything?

I discovered this post:

I'm not sure why, but doing the vector solution solved all my issues. If anyone can expand on why, this would help me a ton!

Other than that, thanks I finally figured it out!!!