< 返回版块

byeblack 发表于 2022-04-18 16:20

Tags:tokio,net

为什么一次扫描耗时需要18秒,有木有办法优化呢?


use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    let host = host.as_str();
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 18s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(time::timeout(
            time::Duration::from_millis(500),
            TcpStream::connect((host, port)),
        ));
    }

    for h in handles {
        if let Ok(Ok(t)) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}
package main

import (
	"fmt"
	"net"
	"os"
	"sync"
	"time"
)

func main() {
	host := "localhost"
	args := os.Args
	if len(args) == 2 {
		host = args[1]
	}
	waitGroup := sync.WaitGroup{}
	start := time.Now()

	fmt.Printf("正在扫描端口连接...%s\n", host)

	for i := 0; i < 65535; i++ {
		waitGroup.Add(1)
		go func(i int) {
			connect(fmt.Sprintf("%s:%d", host, i))
			waitGroup.Done()
		}(i)
	}
	waitGroup.Wait()

	fmt.Println(time.Since(start))
	fmt.Println("输入回车关闭...")
	fmt.Scanln()
}

func connect(addr string) {
	timeout := make(chan bool, 1)
	go func() {
		time.Sleep(500 * time.Millisecond)
		timeout <- true
	}()

	ch := make(chan bool, 1)
	go func() {
		conn, err := net.Dial("tcp", addr)
		if err != nil {
			// fmt.Println("连接失败: ", addr)
			ch <- false
			return
		}
		defer conn.Close()
		ch <- true
	}()

	select {
	case ok := <-ch:
		if ok {
			fmt.Println("端口正常:", addr)
		}
	case <-timeout:
	}
}

评论区

写评论
作者 byeblack 2022-04-20 23:34

好吧,感谢指教

👇
Bai-Jinlin: 你传进去(&str,u16)的时候有引用满足不了spwan的'static约束,你用format就是创建了个没有借用的String满足了'static呀,所以我说就是这个没有什么奇怪的地方

--
👇
byeblack: 不是说Tokio啦,https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html#captured-identifiers-in-format-strings 在这里如果我传值进去就要想办法解决变量host的借用问题,但format宏通过标识符拿到变量,我可以直接将变量传递进去...

--
👇
Bai-Jinlin: 哪里奇怪了?https://docs.rs/tokio/latest/tokio/net/trait.ToSocketAddrs.html

--
👇
byeblack: 非常感谢Grobycn,我又发现个奇怪的事... 1.60.0

use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 7s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(tokio::spawn(time::timeout(
            time::Duration::from_millis(100),
            TcpStream::connect(format!("{host}:{port}")), //format ??? 借用迎刃而解?
        )));
    }

    for h in handles {
        if let Ok(Ok(Ok(t))) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}


ruby 2022-04-20 09:36

你这样 for x in work { do(x).await } 的写法是串行的不会并发执行

用 future::join 并发执行 65535 个请求就快很多

Bai-Jinlin 2022-04-19 18:58

你传进去(&str,u16)的时候有引用满足不了spwan的'static约束,你用format就是创建了个没有借用的String满足了'static呀,所以我说就是这个没有什么奇怪的地方

--
👇
byeblack: 不是说Tokio啦,https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html#captured-identifiers-in-format-strings 在这里如果我传值进去就要想办法解决变量host的借用问题,但format宏通过标识符拿到变量,我可以直接将变量传递进去...

--
👇
Bai-Jinlin: 哪里奇怪了?https://docs.rs/tokio/latest/tokio/net/trait.ToSocketAddrs.html

--
👇
byeblack: 非常感谢Grobycn,我又发现个奇怪的事... 1.60.0

use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 7s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(tokio::spawn(time::timeout(
            time::Duration::from_millis(100),
            TcpStream::connect(format!("{host}:{port}")), //format ??? 借用迎刃而解?
        )));
    }

    for h in handles {
        if let Ok(Ok(Ok(t))) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}


作者 byeblack 2022-04-19 14:35

不是说Tokio啦,https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html#captured-identifiers-in-format-strings 在这里如果我传值进去就要想办法解决变量host的借用问题,但format宏通过标识符拿到变量,我可以直接将变量传递进去...

--
👇
Bai-Jinlin: 哪里奇怪了?https://docs.rs/tokio/latest/tokio/net/trait.ToSocketAddrs.html

--
👇
byeblack: 非常感谢Grobycn,我又发现个奇怪的事... 1.60.0

use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 7s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(tokio::spawn(time::timeout(
            time::Duration::from_millis(100),
            TcpStream::connect(format!("{host}:{port}")), //format ??? 借用迎刃而解?
        )));
    }

    for h in handles {
        if let Ok(Ok(Ok(t))) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}


Bai-Jinlin 2022-04-19 11:23

哪里奇怪了?https://docs.rs/tokio/latest/tokio/net/trait.ToSocketAddrs.html

--
👇
byeblack: 非常感谢Grobycn,我又发现个奇怪的事... 1.60.0

use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 7s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(tokio::spawn(time::timeout(
            time::Duration::from_millis(100),
            TcpStream::connect(format!("{host}:{port}")), //format ??? 借用迎刃而解?
        )));
    }

    for h in handles {
        if let Ok(Ok(Ok(t))) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}


作者 byeblack 2022-04-18 17:50

非常感谢Grobycn,我又发现个奇怪的事... 1.60.0

use std::env;

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

#[tokio::main]
async fn main() {
    let arg = env::args().skip(1);
    let host = match arg.last() {
        Some(x) => x,
        None => "127.0.0.1".to_string(),
    };
    println!("正在扫描端口连接...{}", host);

    let start = tokio::time::Instant::now();
    // 7s
    let mut handles = Vec::new();
    for port in 1..=65535 {
        handles.push(tokio::spawn(time::timeout(
            time::Duration::from_millis(100),
            TcpStream::connect(format!("{host}:{port}")), //format ??? 借用迎刃而解?
        )));
    }

    for h in handles {
        if let Ok(Ok(Ok(t))) = h.await {
            println!("{}", t.peer_addr().unwrap());
        }
    }

    println!("运行时间: {}秒", start.elapsed().as_secs_f32());
    println!("按回车关闭...");

    let mut buf = Vec::new();
    io::stdin().read_buf(&mut buf).await.unwrap();
}


作者 byeblack 2022-04-18 17:42

--
👇
Grobycn: rust 程序没有并发,实际上相当于, timeout 创建的 Future 并没有放入后台调度, 而是后面的 for 循环里面一个一个按顺序运行。

解决办法: 用 tokio::spawn 放入后台运行

handles.push(
    tokio::spawn(
        time::timeout(
            time::Duration::from_millis(500),
            TcpStream::connect((host.to_string(), port)),
        )
    )
);

原来如此,非常感谢! 加了timeout,把spawn给忘了... 效果立竿见影,哈哈 因为现在tokio还不支持数组join_all,futures又不想用,所以有些错愕。

Grobycn 2022-04-18 17:08

rust 程序没有并发,实际上相当于, timeout 创建的 Future 并没有放入后台调度, 而是后面的 for 循环里面一个一个按顺序运行。

解决办法: 用 tokio::spawn 放入后台运行

handles.push(
    tokio::spawn(
        time::timeout(
            time::Duration::from_millis(500),
            TcpStream::connect((host.to_string(), port)),
        )
    )
);
1 共 8 条评论, 1 页