commit 19e607fa6904e3ce0fc0be36f8508311c0c380b7 Author: TaurusXin Date: Mon Apr 22 13:57:55 2024 +0800 init: repository diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c5ac42 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.exe + +tcping \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1aacfac --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Tcping GO + +一个使用go写的小工具,用于检测TCP端口是否开放。 + +## 使用方法 + +```shell +tcping -h + +Usage of tcping: + -c, --count int 测试次数,默认为4次 (default 4) + -h, --help 显示帮助信息 + -t, --infinite 无限次测试 + -6, --ipv6 使用 IPv6,需搭配域名使用 + -p, --port int 端口,默认为80 (default 80) + -s, --timeout duration 超时时间,默认为2秒 (default 2s) +``` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..eb4e2f9 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module tcping + +go 1.22.1 + +require github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..287f6fa --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= diff --git a/main.go b/main.go new file mode 100644 index 0000000..c257b96 --- /dev/null +++ b/main.go @@ -0,0 +1,142 @@ +package main + +import ( + flag "github.com/spf13/pflag" + "fmt" + "net" + "os" + "os/signal" + "syscall" + "time" +) + +func main() { + var ( + port int + count int + timeoutDuration time.Duration + infinite bool + ipv6 bool + + successCount int + attemptCount int + stopped bool + ) + + // 设置命令行参数 + help := flag.BoolP("help", "h", false, "显示帮助信息") + flag.IntVarP(&port, "port", "p", 80, "端口,默认为80") + flag.IntVarP(&count, "count", "c", 4, "测试次数,默认为4次") + flag.DurationVarP(&timeoutDuration, "timeout", "s", 2*time.Second, "超时时间,默认为2秒") + flag.BoolVarP(&infinite, "infinite", "t", false, "无限次测试") + flag.BoolVarP(&ipv6, "ipv6", "6", false, "使用 IPv6,需搭配域名使用") + + flag.Parse() + + if *help { + flag.Usage() + os.Exit(0) + } + + args := flag.Args() + if len(args) < 1 { + flag.Usage() + os.Exit(1) + } + + if port < 1 || port > 65535 { + fmt.Println("端口号必须在1-65535之间") + os.Exit(1) + } + + hostname := args[0] + + // 设置信号捕获 + stopped = false + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + ip := "" + + if net.ParseIP(hostname) == nil { + // 解析域名 + ips, err := net.LookupIP(hostname) + if err != nil { + fmt.Printf("解析 %s 失败: %s\n", hostname, err) + os.Exit(1) + } + record := "A" + if ipv6 { + record = "AAAA" + } + ip, err = filterIP(ips, ipv6) + if (err != nil) { + fmt.Printf("找不到 %s 的 %s 记录\n", hostname, record) + os.Exit(1) + } + fmt.Printf("使用 %s 的 %s 记录: %s\n", hostname, record, ip) + } else { + ip = hostname + } + + address := net.JoinHostPort(ip, fmt.Sprintf("%d", port)) + + if infinite { + count = -1 + } + + done := make(chan bool, 1) + go func() { + for i := 0; infinite || i < count; i++ { + start := time.Now() + conn, err := net.DialTimeout("tcp", address, timeoutDuration) + duration := time.Since(start) + attemptCount++ + + if err != nil { + fmt.Printf("测试到 %s 的连接失败: %s\n", address, "连接超时") + } else { + successCount++ + fmt.Printf("[%d] 来自 %s 的响应: 时间=%s\n",i+1, address, fmt.Sprintf("%.3fms", float64(duration)/float64(time.Millisecond))) + conn.Close() + } + + if !infinite && attemptCount >= count { + break + } + time.Sleep(1 * time.Second) + } + done <- true + }() + + // 等待信号或测试完成 + select { + case <-sigChan: + fmt.Println("\n测试被用户中断") + stopped = true + case <-done: + } + + // 打印统计结果 + if !stopped { + fmt.Println() + } + fmt.Printf("测试完成,成功次数: %d/%d\n", successCount, attemptCount) +} + +func filterIP(ips []net.IP, ipv6 bool) (string, error) { + if ipv6 { + for _, ip := range ips { + if ip.To16() != nil && ip.To4() == nil { + return ip.String(), nil + } + } + } else { + for _, ip := range ips { + if ip.To4() != nil && ip.To16() != nil { + return ip.String(), nil + } + } + } + return "", fmt.Errorf("找不到合适的IP地址") +}