main.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "sort"
  8. "strconv"
  9. "sync"
  10. "time"
  11. "github.com/cheggaaa/pb/v3"
  12. )
  13. var version string
  14. var disableDownload bool
  15. var tcpPort int
  16. var ipFile string
  17. var outputFile string
  18. var printResultNum int
  19. var timeLimit int
  20. var speedLimit int
  21. func init() {
  22. var downloadSecond int64
  23. var printVersion bool
  24. var help = `
  25. CloudflareSpeedTest ` + version + `
  26. 测试 Cloudflare CDN 所有 IP 的延迟和速度,获取最快 IP!
  27. https://github.com/XIU2/CloudflareSpeedTest
  28. 参数:
  29. -n 500
  30. 测速线程数量;数值越大速度越快,请勿超过 1000(结果误差大);(默认 500)
  31. -t 4
  32. 延迟测速次数;单个 IP 测速次数,为 1 时将过滤丢包的IP,TCP协议;(默认 4)
  33. -tp 443
  34. 延迟测速端口;延迟测速 TCP 协议的端口;(默认 443)
  35. -dn 20
  36. 下载测速数量;延迟测速并排序后,从最低延迟起下载测速数量,请勿太多(速度慢);(默认 20)
  37. -dt 5
  38. 下载测速时间;单个 IP 测速最长时间,单位:秒;(默认 5)
  39. -url https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png
  40. 下载测速地址;用来 Cloudflare CDN 测速的文件地址,如含有空格请加上引号;
  41. -tl 200
  42. 延迟时间上限;只输出指定延迟时间以下的结果,数量为 -dn 参数的值,单位:ms;
  43. -sl 5
  44. 下载速度下限;只输出指定下载速度以上的结果,数量为 -dn 参数的值,单位:MB/s;
  45. -p 20
  46. 显示结果数量;测速后直接显示指定数量的结果,值为 0 时不显示结果直接退出;(默认 20)
  47. -f ip.txt
  48. IP 数据文件;如含有空格请加上引号;支持其他 CDN IP段,记得禁用下载测速;(默认 ip.txt)
  49. -o result.csv
  50. 输出结果文件;如含有空格请加上引号;为空格时不输出结果文件(-o " ");允许其他后缀;(默认 result.csv)
  51. -dd
  52. 禁用下载测速;如果带上该参数就是禁用下载测速;(默认 启用)
  53. -v
  54. 打印程序版本
  55. -h
  56. 打印帮助说明`
  57. flag.IntVar(&pingRoutine, "n", 500, "测速线程数量")
  58. flag.IntVar(&pingTime, "t", 4, "延迟测速次数")
  59. flag.IntVar(&tcpPort, "tp", 443, "延迟测速端口")
  60. flag.IntVar(&downloadTestCount, "dn", 20, "下载测速数量")
  61. flag.Int64Var(&downloadSecond, "dt", 5, "下载测速时间")
  62. flag.StringVar(&url, "url", "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png", "下载测速地址")
  63. flag.IntVar(&timeLimit, "tl", 0, "延迟时间上限")
  64. flag.IntVar(&speedLimit, "sl", 0, "下载速度下限")
  65. flag.IntVar(&printResultNum, "p", 20, "显示结果数量")
  66. flag.BoolVar(&disableDownload, "dd", false, "禁用下载测速")
  67. flag.StringVar(&ipFile, "f", "ip.txt", "IP 数据文件")
  68. flag.StringVar(&outputFile, "o", "result.csv", "输出结果文件")
  69. flag.BoolVar(&printVersion, "v", false, "打印程序版本")
  70. downloadTestTime = time.Duration(downloadSecond) * time.Second
  71. flag.Usage = func() { fmt.Print(help) }
  72. flag.Parse()
  73. if printVersion {
  74. println(version)
  75. os.Exit(0)
  76. }
  77. if pingRoutine <= 0 {
  78. pingRoutine = 500
  79. }
  80. if pingTime <= 0 {
  81. pingTime = 4
  82. }
  83. if tcpPort < 1 || tcpPort > 65535 {
  84. tcpPort = 443
  85. }
  86. if downloadTestCount <= 0 {
  87. downloadTestCount = 20
  88. }
  89. if downloadSecond <= 0 {
  90. downloadSecond = 10
  91. }
  92. if url == "" {
  93. url = "https://cf.xiu2.xyz/Github/CloudflareSpeedTest.png"
  94. }
  95. if timeLimit <= 0 {
  96. timeLimit = 9999
  97. }
  98. if speedLimit < 0 {
  99. speedLimit = 0
  100. }
  101. if printResultNum < 0 {
  102. printResultNum = 20
  103. }
  104. if ipFile == "" {
  105. ipFile = "ip.txt"
  106. }
  107. if outputFile == " " {
  108. outputFile = ""
  109. }
  110. }
  111. func main() {
  112. initRandSeed() // 置随机数种子
  113. failTime = pingTime // 设置接收次数
  114. ips := loadFirstIPOfRangeFromFile(ipFile) // 读入IP
  115. pingCount := len(ips) * pingTime // 计算进度条总数(IP*测试次数)
  116. bar := pb.Simple.Start(pingCount) // 进度条总数
  117. var wg sync.WaitGroup
  118. var mu sync.Mutex
  119. var data = make([]CloudflareIPData, 0)
  120. var data_2 = make([]CloudflareIPData, 0)
  121. fmt.Println("# XIU2/CloudflareSpeedTest " + version + "\n")
  122. fmt.Println("开始延迟测速(模式:TCP,端口:" + strconv.Itoa(tcpPort) + "):")
  123. control := make(chan bool, pingRoutine)
  124. for _, ip := range ips {
  125. wg.Add(1)
  126. control <- false
  127. handleProgress := handleProgressGenerator(bar) // 多线程进度条
  128. go tcpingGoroutine(&wg, &mu, ip, tcpPort, pingTime, &data, control, handleProgress)
  129. }
  130. wg.Wait()
  131. bar.Finish()
  132. sort.Sort(CloudflareIPDataSet(data)) // 排序
  133. // 下载测速
  134. if !disableDownload { // 如果禁用下载测速就跳过
  135. if len(data) > 0 { // IP数组长度(IP数量) 大于 0 时继续
  136. if len(data) < downloadTestCount { // 如果IP数组长度(IP数量) 小于 下载测速次数,则次数改为IP数
  137. //fmt.Println("\n[信息] IP 数量小于下载测速次数(" + strconv.Itoa(downloadTestCount) + " < " + strconv.Itoa(len(data)) + "),下载测速次数改为IP数。\n")
  138. downloadTestCount = len(data)
  139. }
  140. var downloadTestCount_2 int // 临时的下载测速次数
  141. if timeLimit == 9999 && speedLimit == 0 {
  142. downloadTestCount_2 = downloadTestCount // 如果没有指定条件,则临时的下载次数变量为下载测速次数
  143. fmt.Println("开始下载测速:")
  144. } else if timeLimit > 0 || speedLimit >= 0 {
  145. downloadTestCount_2 = len(data) // 如果指定了任意一个条件,则临时的下载次数变量改为总数量
  146. fmt.Println("开始下载测速(延迟时间上限:" + strconv.Itoa(timeLimit) + " ms,下载速度下限:" + strconv.Itoa(speedLimit) + " MB/s):")
  147. }
  148. bar = pb.Simple.Start(downloadTestCount_2)
  149. for i := 0; i < downloadTestCount_2; i++ {
  150. _, speed := DownloadSpeedHandler(data[i].ip)
  151. data[i].downloadSpeed = speed
  152. bar.Add(1)
  153. if int(data[i].pingTime) <= timeLimit && int(float64(speed)/1024/1024) >= speedLimit {
  154. data_2 = append(data_2, data[i]) // 延迟和速度均满足条件时,添加到新数组中
  155. if len(data_2) == downloadTestCount { // 满足条件的 IP =下载测速次数,则跳出循环
  156. break
  157. }
  158. } else if int(data[i].pingTime) > timeLimit {
  159. break
  160. }
  161. }
  162. bar.Finish()
  163. } else {
  164. fmt.Println("\n[信息] IP数量为 0,跳过下载测速。")
  165. }
  166. }
  167. if len(data_2) > 0 { // 如果该数字有内容,说明进行过指定条件的下载测速
  168. if outputFile != "" {
  169. ExportCsv(outputFile, data_2) // 输出结果到文件(指定延迟时间或下载速度的)
  170. }
  171. printResult(data_2) // 显示最快结果(指定延迟时间或下载速度的)
  172. } else {
  173. if outputFile != "" {
  174. ExportCsv(outputFile, data) // 输出结果到文件
  175. }
  176. printResult(data) // 显示最快结果
  177. }
  178. }
  179. // 显示最快结果
  180. func printResult(data []CloudflareIPData) {
  181. sysType := runtime.GOOS
  182. if printResultNum > 0 { // 如果禁止直接输出结果就跳过
  183. dateString := convertToString(data) // 转为多维数组 [][]String
  184. if len(dateString) > 0 { // IP数组长度(IP数量) 大于 0 时继续
  185. if len(dateString) < printResultNum { // 如果IP数组长度(IP数量) 小于 打印次数,则次数改为IP数量
  186. //fmt.Println("\n[信息] IP 数量小于显示结果数量(" + strconv.Itoa(printResultNum) + " < " + strconv.Itoa(len(dateString)) + "),显示结果数量改为IP数量。\n")
  187. printResultNum = len(dateString)
  188. }
  189. fmt.Printf("%-16s%-5s%-5s%-5s%-6s%-11s\n", "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度 (MB/s)")
  190. for i := 0; i < printResultNum; i++ {
  191. fmt.Printf("%-18s%-8s%-8s%-8s%-10s%-15s\n", ipPadding(dateString[i][0]), dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5])
  192. }
  193. if sysType == "windows" { // 如果是 Windows 系统,则需要按下 回车键 或 Ctrl+C 退出
  194. if outputFile != "" {
  195. fmt.Printf("\n完整测速结果已写入 %v 文件,请使用记事本/表格软件查看。\n按下 回车键 或 Ctrl+C 退出。", outputFile)
  196. } else {
  197. fmt.Printf("\n按下 回车键 或 Ctrl+C 退出。")
  198. }
  199. var pause int
  200. fmt.Scanln(&pause)
  201. } else { // 其它系统直接退出
  202. if outputFile != "" {
  203. fmt.Println("\n完整测速结果已写入 " + outputFile + " 文件,请使用记事本/表格软件查看。")
  204. }
  205. }
  206. } else {
  207. fmt.Println("\n[信息] IP数量为 0,跳过输出结果。")
  208. }
  209. } else {
  210. fmt.Println("\n完整测速结果已写入 " + outputFile + " 文件,请使用记事本/表格软件查看。")
  211. }
  212. }