Remote port scan really slow and synchronous despite tokio?

I am trying to use tokio and async scan remote ports. Open hosts return nearly instantly, but However, the result is that closed ports takes 3-5 seconds to fail and they're returning in order, so somehow it feels like I'm doing something wrong, but I don't see what . . .

println!("{}","starting");
for i in 1..254{
    let ip = "10.0.3.";
    let it = tokio::spawn(
        async move{
            let f = format!("{}{}:{}",ip.clone(),i,"80");
            let listen =  TcpStream::connect(f.clone()).await;
            match listen{
                Ok(a)=>{
                    println!("{}",&f);
                },
                Err(e)=>{println!("{}, {}",&f,e)}
            }
        });
        it.await;
}
println!("Done");

if you want concurrency, you should not have the line it.await at the end of the loop. Waiting for the handle returned by spawn will prevent the loop from looping an proceeding to the next port.

I put that there, because when I didn't the program closes before everything resolves. Done Prints, but not one of there errors gets a chance to print out.

How do keep everything awake until everything resolves without that?

you can use a utility like FuturesUnordered to wait for all of the futures to complete concurrently.

Would it make more sense to use channels and have each thread send messages to the channel and print the channel? Would something like that work? Just trying to make sense of how to tie it all together with concurrency.

You have to spawn one global task, and then inside that task use an iterator that makes 254 Future objects, and join_all or FuturesUnordered or buffered Stream to wait for them concurrently.

.await waits for one future at a time, so you need a do-254-things-together future to wait for.

For this specific case, you should just put the JoinHandles returned by tokio::spawn into a vector, then loop over the vector after the loop.

let mut jhs = Vec::new();
for i in 1..254 {
    let jh = tokio::spawn(...);
    jhs.push(jh);
}
for jh in jhs {
    jh.await.unwrap();
}

You don't need to mess around with FuturesUnordered or join_all when using tokio::spawn.

2 Likes

That works! Alice, you are amazing.

It's still slow, but the async/multithread works so it's dramatically faster. I'm guessing what's slowing it down now is something in the tokio::net library. I'm guessing there's some delay built in for it to test for a definitely closed/blocked port. I'm not sure where to look for that, I think it's beyond my ability to find that right now.

What kind of slowdown are you expecting from tokio::net? It doesn't do anything fancy or expensive.

What I'm experiencing before and now is about a 3-5 second per failed connection time. Since it's now multithreaded it's like 8x speed on my pc, but at 254 tested connections that's a couple minutes. On a class A that's a lifetime . . .

I can't imagine a reason that a failed network connection should take 3-5 seconds to confirm.

I strongly doubt that this is tokio::net's fault.

You might be right. I'm going to start a new topic for it. Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.