编写端口扫描遇到的问题

最近在学习并发编程就尝试着写个端口扫描器(PS:仅用于学习用途)

Q(修订1.1):

  1. 我想把扫描可用的端口保存到opening.txt中,所以在case语句中写了个保存并写入的操作,但是执行起来程序会阻塞,这是什么原因,是文件资源没有关闭?后面跟了return难道不能结束
  2. 就是最佳答案修改后的代码中的疑惑,isOpen()中没有实例化chan,为什么在main中需要进行实例化,定义之后不是可以直接用吗?
  3. 最后就是,为什么实例化chan的代码片段在isOpen()之前,这样循环出来得到的chan是空的吧,不应该是放在isOpen()之后等拿到了open端口再range这个chan中的数据吗?请指教

编写端口扫描遇到的问题

Code

package main

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

var useful chan string

func main() {
    var wg sync.WaitGroup
    url := "127.0.0.1"
    from := 3305
    to := 3306

    // 【问题1】 如果时间太短,无法完成 TCP 请求
    // var duration time.Duration = 200 // 默认 纳秒单位
    var duration = 2 * time.Second // 设置 2s

    // 【问题2】 chanel 没有初始化,无法正常使用
    useful = make(chan string, to-from+1) // 创建 chan 缓冲区
    defer close(useful)

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 【补充1】可以获取 channel 中 ,可连接的端口
    go func() {
        for result := range useful {
            fmt.Println(result)
        }
    }()

    for i := from; i <= to; i++ {
        addr := fmt.Sprintf("%s:%d", url, i)

        wg.Add(1)
        go func(ctx2 context.Context) {
            isOpen(ctx2, addr, duration)
            wg.Done()
            return
        }(ctx)

        // 【问题3】 如果在这里 wait 那么将是依次执行 goroutinue ,无法利用到 并发请求
        // wg.Wait()
    }

    // 【问题3】 放到这里,则是并发请求,等待所有 goroutinue 结束,但是结果会乱序
    wg.Wait()

}

func isOpen(ctx context.Context, addr string, duration time.Duration) {
    conn, err := net.DialTimeout("tcp", addr, duration)
    if err != nil {
        log.Printf("port %s is closed. err:%s duration:%s", addr, err, duration)
        return
    }
    defer func(conn net.Conn) {
        err := conn.Close()
        if err != nil {
            return
        }
    }(conn)

    select {
    case <-ctx.Done():
        fmt.Println("over...")
        return
    case useful <- addr:
        log.Printf("port %s is openning", addr)
        err := os.WriteFile("opening.txt", []byte(<-useful), os.ModePerm)
        if err != nil {
            return
        }
        return
    }
}
最佳答案
  • 最关键的问题
    • 【问题1】 请求超时时间太短
    • 【问题2】channel 未初始化
  • 可参考
  • 针对你的代码进行部分修改
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "sync"
    "time"
)

var useful chan string

func main() {
    var wg sync.WaitGroup
    url := "127.0.0.1"
    from := 80
    to := 100

    // 【问题1】 如果时间太短,无法完成 TCP 请求
    // var duration time.Duration = 200 // 默认 纳秒单位
    var duration time.Duration = 2 * time.Second // 设置 2s

    // 【问题2】 chanel 没有初始化,无法正常使用
    useful = make(chan string, to-from+1) // 创建 chan 缓冲区
    defer close(useful)

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 【补充1】可以获取 channel 中 ,可连接的端口
    go func() {
        for result := range useful {
            fmt.Println(result)
        }
    }()

    for i := from; i <= to; i++ {
        addr := fmt.Sprintf("%s:%d", url, i)

        wg.Add(1)
        go func(ctx2 context.Context) {
            isOpen(ctx2, addr, duration)
            wg.Done()
            return
        }(ctx)

        // 【问题3】 如果在这里 wait 那么将是依次执行 goroutinue ,无法利用到 并发请求
        // wg.Wait()
    }

    // 【问题3】 放到这里,则是并发请求,等待所有 goroutinue 结束,但是结果会乱序
    wg.Wait()

}

func isOpen(ctx context.Context, addr string, duration time.Duration) {
    conn, err := net.DialTimeout("tcp", addr, duration)
    if err != nil {
        log.Printf("port %s is closed. err:%s duration:%s", addr, err, duration)
        return
    }
    defer func(conn net.Conn) {
        err := conn.Close()
        if err != nil {
            return
        }
    }(conn)

    select {
    case <-ctx.Done():
        fmt.Println("over...")
        return
    case useful <- addr:
        log.Printf("port %s is openning", addr)
        return
    }
}
10个月前 评论
Scrooge (楼主) 10个月前
Scrooge (楼主) 10个月前
Scrooge (楼主) 10个月前
Tacks (作者) 10个月前
讨论数量: 9
  • 最关键的问题
    • 【问题1】 请求超时时间太短
    • 【问题2】channel 未初始化
  • 可参考
  • 针对你的代码进行部分修改
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "sync"
    "time"
)

var useful chan string

func main() {
    var wg sync.WaitGroup
    url := "127.0.0.1"
    from := 80
    to := 100

    // 【问题1】 如果时间太短,无法完成 TCP 请求
    // var duration time.Duration = 200 // 默认 纳秒单位
    var duration time.Duration = 2 * time.Second // 设置 2s

    // 【问题2】 chanel 没有初始化,无法正常使用
    useful = make(chan string, to-from+1) // 创建 chan 缓冲区
    defer close(useful)

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 【补充1】可以获取 channel 中 ,可连接的端口
    go func() {
        for result := range useful {
            fmt.Println(result)
        }
    }()

    for i := from; i <= to; i++ {
        addr := fmt.Sprintf("%s:%d", url, i)

        wg.Add(1)
        go func(ctx2 context.Context) {
            isOpen(ctx2, addr, duration)
            wg.Done()
            return
        }(ctx)

        // 【问题3】 如果在这里 wait 那么将是依次执行 goroutinue ,无法利用到 并发请求
        // wg.Wait()
    }

    // 【问题3】 放到这里,则是并发请求,等待所有 goroutinue 结束,但是结果会乱序
    wg.Wait()

}

func isOpen(ctx context.Context, addr string, duration time.Duration) {
    conn, err := net.DialTimeout("tcp", addr, duration)
    if err != nil {
        log.Printf("port %s is closed. err:%s duration:%s", addr, err, duration)
        return
    }
    defer func(conn net.Conn) {
        err := conn.Close()
        if err != nil {
            return
        }
    }(conn)

    select {
    case <-ctx.Done():
        fmt.Println("over...")
        return
    case useful <- addr:
        log.Printf("port %s is openning", addr)
        return
    }
}
10个月前 评论
Scrooge (楼主) 10个月前
Scrooge (楼主) 10个月前
Scrooge (楼主) 10个月前
Tacks (作者) 10个月前

修订 1.1 的三个问题

  • channel 的用法,读取和写入的机制

  • 变量的作用域,channel 的初始化,没有初始化的 channel ,直接读取或者写入会造成 死锁 deadlock

  • channel 读取数据的阻塞机制 <-ch,goroutinue 中如果没有读取数据是会阻塞等待数据到达的。

    最后, os.WriteFile 用法是将一个字节数组写入到指定的文件 ,应该是 遍历 for range channel 然后,一个个地址写入

10个月前 评论
Scrooge (楼主) 10个月前
Tacks (作者) 10个月前
Scrooge (楼主) 10个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!