Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/1.7.0 Pack #195

Merged
merged 13 commits into from
May 4, 2024
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,27 @@ Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
-l, --list Show available speedtest.net servers.
-s, --server=SERVER ... Select server id to speedtest.
--custom-url=CUSTOM-URL Specify the url of the server instead of getting a list from speedtest.net.
--custom-url=CUSTOM-URL Specify the url of the server instead of fetching from speedtest.net.
--saving-mode Test with few resources, though low accuracy (especially > 30Mbps).
--json Output results in json format.
--location=LOCATION Change the location with a precise coordinate.
--location=LOCATION Change the location with a precise coordinate (format: lat,lon).
--city=CITY Change the location with a predefined city label.
--city-list List all predefined city labels.
--proxy=PROXY Set a proxy(http[s] or socks) for the speedtest.
eg: --proxy=socks://10.20.0.101:7890
eg: --proxy=http://10.20.0.101:7890
--source=SOURCE Bind a source interface for the speedtest.
--dns-bind-source DNS request binding source.(Experimental)
--dns-bind-source DNS request binding source (experimental).
eg: --source=10.20.0.101
-m --multi Enable multi-server mode.
-t --thread=THREAD Set the number of concurrent connections.
--search=SEARCH Fuzzy search servers by a keyword.
--ua Set the user-agent header for the speedtest.
--no-download Disable download test.
--no-upload Disable upload test.
--ping-mode Select a method for Ping. (support icmp/tcp/http)
--ping-mode Select a method for Ping (support icmp/tcp/http).
-u --unit Set human-readable and auto-scaled rate units for output
(options: decimal-bits/decimal-bytes/binary-bits/binary-bytes).
-d --debug Enable debug mode.
--version Show application version.
```
Expand All @@ -68,15 +70,15 @@ Simply use `speedtest` command. The closest server is selected by default. Use t
```bash
$ speedtest

speedtest-go v1.6.5 @showwin
speedtest-go v1.7.0 @showwin

✓ ISP: 124.27.199.165 (Fujitsu) [34.9769, 138.3831]
✓ Found 20 Public Servers

✓ Test Server: [6691] 9.03km Shizuoka (Japan) by sudosan
✓ Latency: 24.15396ms Jitter: 777.465µs Min: 22.8926ms Max: 25.5387ms
✓ Download: 73.30Mbps (used: 101.48MB)
✓ Upload: 35.26Mbps (used: 47.33MB)
✓ Latency: 4.452963ms Jitter: 41.271µs Min: 4.395179ms Max: 4.517576ms
✓ Download: 115.52 Mbps (Used: 135.75MB) (Latency: 4ms Jitter: 0ms Min: 4ms Max: 4ms)
✓ Upload: 4.02 Mbps (Used: 6.85MB) (Latency: 4ms Jitter: 1ms Min: 3ms Max: 8ms)
```

#### Test with Other Servers
Expand All @@ -98,23 +100,20 @@ and select them by id.
```bash
$ speedtest --server 6691 --server 6087

speedtest-go v1.6.5 @showwin
speedtest-go v1.7.0 @showwin

✓ ISP: 124.27.199.165 (Fujitsu) [34.9769, 138.3831]
✓ Found 20 Public Servers
✓ Found 2 Specified Public Server(s)

✓ Test Server: [6691] 9.03km Shizuoka (Japan) by sudosan
✓ Latency: 21.424ms Jitter: 1.644ms Min: 19.142ms Max: 23.926ms
✓ Download: 65.82Mbps (used: 75.48MB)
✓ Upload: 27.00Mbps (used: 36.33MB)
✓ Download: 65.82Mbps (Used: 75.48MB) (Latency: 22ms Jitter: 2ms Min: 17ms Max: 24ms)
✓ Upload: 27.00Mbps (Used: 36.33MB) (Latency: 23ms Jitter: 2ms Min: 18ms Max: 25ms)

✓ Test Server: [6087] 120.55km Fussa-shi (Japan) by Allied Telesis Capital Corporation
✓ Latency: 38.694699ms Jitter: 2.724ms Min: 36.443ms Max: 39.953ms
✓ Download: 72.24Mbps (used: 83.72MB)
✓ Upload: 29.56Mbps (used: 47.64MB)

Download Avg: 69.03 Mbit/s
Upload Avg: 28.28 Mbit/s
✓ Download: 72.24Mbps (Used: 83.72MB) (Latency: 37ms Jitter: 3ms Min: 36ms Max: 40ms)
✓ Upload: 29.56Mbps (Used: 47.64MB) (Latency: 38ms Jitter: 3ms Min: 37ms Max: 41ms)
```

#### Test with a virtual location
Expand Down Expand Up @@ -195,7 +194,8 @@ func main() {
s.PingTest(nil)
s.DownloadTest()
s.UploadTest()
fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed)
// Note: The unit of s.DLSpeed, s.ULSpeed is bytes per second, this is a float64.
fmt.Printf("Latency: %s, Download: %s, Upload: %s\n", s.Latency, s.DLSpeed, s.ULSpeed)
s.Context.Reset() // reset counter
}
}
Expand Down
3 changes: 2 additions & 1 deletion example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func main() {
checkError(s.DownloadTest())
checkError(s.UploadTest())

fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed)
// Note: The unit of s.DLSpeed, s.ULSpeed is bytes per second, this is a float64.
fmt.Printf("Latency: %s, Download: %s, Upload: %s\n", s.Latency, s.DLSpeed, s.ULSpeed)
s.Context.Reset()
}
}
Expand Down
164 changes: 101 additions & 63 deletions speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"strconv"
"strings"
"sync/atomic"
"time"

"github.com/showwin/speedtest-go/speedtest"
Expand All @@ -15,22 +16,23 @@ import (
var (
showList = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool()
serverIds = kingpin.Flag("server", "Select server id to run speedtest.").Short('s').Ints()
customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of getting a list from speedtest.net.").String()
customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of fetching from speedtest.net.").String()
savingMode = kingpin.Flag("saving-mode", "Test with few resources, though low accuracy (especially > 30Mbps).").Bool()
jsonOutput = kingpin.Flag("json", "Output results in json format.").Bool()
location = kingpin.Flag("location", "Change the location with a precise coordinate. Format: lat,lon").String()
location = kingpin.Flag("location", "Change the location with a precise coordinate (format: lat,lon).").String()
city = kingpin.Flag("city", "Change the location with a predefined city label.").String()
showCityList = kingpin.Flag("city-list", "List all predefined city labels.").Bool()
proxy = kingpin.Flag("proxy", "Set a proxy(http[s] or socks) for the speedtest.").String()
source = kingpin.Flag("source", "Bind a source interface for the speedtest.").String()
dnsBindSource = kingpin.Flag("dns-bind-source", "DNS request binding source.(Experimental)").Bool()
dnsBindSource = kingpin.Flag("dns-bind-source", "DNS request binding source (experimental).").Bool()
multi = kingpin.Flag("multi", "Enable multi-server mode.").Short('m').Bool()
thread = kingpin.Flag("thread", "Set the number of concurrent connections.").Short('t').Int()
search = kingpin.Flag("search", "Fuzzy search servers by a keyword.").String()
userAgent = kingpin.Flag("ua", "Set the user-agent header for the speedtest.").String()
noDownload = kingpin.Flag("no-download", "Disable download test.").Bool()
noUpload = kingpin.Flag("no-upload", "Disable upload test.").Bool()
pingMode = kingpin.Flag("ping-mode", "Select a method for Ping. (support icmp/tcp/http)").Default("http").String()
pingMode = kingpin.Flag("ping-mode", "Select a method for Ping (support icmp/tcp/http).").Default("http").String()
unit = kingpin.Flag("unit", "Set human-readable and auto-scaled rate units for output (options: decimal-bits/decimal-bytes/binary-bits/binary-bytes).").Short('u').String()
debug = kingpin.Flag("debug", "Enable debug mode.").Short('d').Bool()
)

Expand All @@ -40,23 +42,25 @@ func main() {
kingpin.Parse()
AppInfo()

speedtest.SetUnit(parseUnit(*unit))

// 0. speed test setting
var speedtestClient = speedtest.New(speedtest.WithUserConfig(
&speedtest.UserConfig{
UserAgent: *userAgent,
Proxy: *proxy,
Source: *source,
DnsBindSource: *dnsBindSource,
Debug: *debug,
PingMode: parseProto(*pingMode), // TCP as default
SavingMode: *savingMode,
CityFlag: *city,
LocationFlag: *location,
Keyword: *search,
NoDownload: *noDownload,
NoUpload: *noUpload,
UserAgent: *userAgent,
Proxy: *proxy,
Source: *source,
DnsBindSource: *dnsBindSource,
Debug: *debug,
PingMode: parseProto(*pingMode), // TCP as default
SavingMode: *savingMode,
MaxConnections: *thread,
CityFlag: *city,
LocationFlag: *location,
Keyword: *search,
NoDownload: *noDownload,
NoUpload: *noUpload,
}))
speedtestClient.SetNThread(*thread)

if *showCityList {
speedtest.PrintCityList()
Expand Down Expand Up @@ -124,75 +128,46 @@ func main() {
task.Printf("Latency: %v Jitter: %v Min: %v Max: %v", server.Latency, server.Jitter, server.MinLatency, server.MaxLatency)
task.Complete()
})

accEcho := newAccompanyEcho(server, time.Millisecond*500)
taskManager.Run("Download", func(task *Task) {
var latencies []int64
var lc int64
quit := false
go func() {
for {
if quit {
return
}
latency, err1 := server.HTTPPing(context.Background(), 1, time.Millisecond*500, nil)
if err1 != nil {
continue
}
lc = latency[0]
latencies = append(latencies, latency...)
}
}()
ticker := speedtestClient.CallbackDownloadRate(func(downRate float64) {
accEcho.Run()
speedtestClient.SetCallbackDownload(func(downRate speedtest.ByteRate) {
lc := accEcho.CurrentLatency()
if lc == 0 {
task.Printf("Download: %.2fMbps (latency: --)", downRate)
task.Printf("Download: %s (Latency: --)", downRate)
} else {
task.Printf("Download: %.2fMbps (latency: %dms)", downRate, lc/1000000)
task.Printf("Download: %s (Latency: %dms)", downRate, lc/1000000)
}
})
if *multi {
task.CheckError(server.MultiDownloadTestContext(context.Background(), servers))
} else {
task.CheckError(server.DownloadTest())
}
ticker.Stop()
mean, _, std, minL, maxL := speedtest.StandardDeviation(latencies)
task.Printf("Download: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.DLSpeed, float64(server.Context.Manager.GetTotalDownload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
accEcho.Stop()
mean, _, std, minL, maxL := speedtest.StandardDeviation(accEcho.Latencies())
task.Printf("Download: %s (Used: %.2fMB) (Latency: %dms Jitter: %dms Min: %dms Max: %dms)", server.DLSpeed, float64(server.Context.Manager.GetTotalDownload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})

taskManager.Run("Upload", func(task *Task) {
var latencies []int64
var lc int64
quit := false
go func() {
for {
if quit {
return
}
latency, err1 := server.HTTPPing(context.Background(), 1, time.Millisecond*500, nil)
if err1 != nil {
continue
}
lc = latency[0]
latencies = append(latencies, latency...)
}
}()
ticker := speedtestClient.CallbackUploadRate(func(upRate float64) {
accEcho.Run()
speedtestClient.SetCallbackUpload(func(upRate speedtest.ByteRate) {
lc := accEcho.CurrentLatency()
if lc == 0 {
task.Printf("Upload: %.2fMbps (latency: --)", upRate)
task.Printf("Upload: %s (Latency: --)", upRate)
} else {
task.Printf("Upload: %.2fMbps (latency: %dms)", upRate, lc/1000000)
task.Printf("Upload: %s (Latency: %dms)", upRate, lc/1000000)
}
})
if *multi {
task.CheckError(server.MultiUploadTestContext(context.Background(), servers))
} else {
task.CheckError(server.UploadTest())
}
ticker.Stop()
quit = true
mean, _, std, minL, maxL := speedtest.StandardDeviation(latencies)
task.Printf("Upload: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.ULSpeed, float64(server.Context.Manager.GetTotalUpload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
accEcho.Stop()
mean, _, std, minL, maxL := speedtest.StandardDeviation(accEcho.Latencies())
task.Printf("Upload: %s (Used: %.2fMB) (Latency: %dms Jitter: %dms Min: %dms Max: %dms)", server.ULSpeed, float64(server.Context.Manager.GetTotalUpload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})
taskManager.Reset()
Expand All @@ -210,6 +185,54 @@ func main() {
}
}

type AccompanyEcho struct {
stopEcho chan bool
server *speedtest.Server
currentLatency int64
interval time.Duration
latencies []int64
}

func newAccompanyEcho(server *speedtest.Server, interval time.Duration) *AccompanyEcho {
return &AccompanyEcho{
server: server,
interval: interval,
stopEcho: make(chan bool),
}
}

func (ae *AccompanyEcho) Run() {
ae.latencies = make([]int64, 0)
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ae.stopEcho:
cancel()
return
default:
latency, _ := ae.server.HTTPPing(ctx, 1, ae.interval, nil)
if len(latency) > 0 {
atomic.StoreInt64(&ae.currentLatency, latency[0])
ae.latencies = append(ae.latencies, latency[0])
}
}
}
}()
}

func (ae *AccompanyEcho) Stop() {
ae.stopEcho <- false
}

func (ae *AccompanyEcho) CurrentLatency() int64 {
return atomic.LoadInt64(&ae.currentLatency)
}

func (ae *AccompanyEcho) Latencies() []int64 {
return ae.latencies
}

func showServerList(servers speedtest.Servers) {
for _, s := range servers {
fmt.Printf("[%5s] %9.2fkm ", s.ID, s.Distance)
Expand All @@ -223,6 +246,21 @@ func showServerList(servers speedtest.Servers) {
}
}

func parseUnit(str string) speedtest.UnitType {
str = strings.ToLower(str)
if str == "decimal-bits" {
return speedtest.UnitTypeDecimalBits
} else if str == "decimal-bytes" {
return speedtest.UnitTypeDecimalBytes
} else if str == "binary-bits" {
return speedtest.UnitTypeBinaryBits
} else if str == "binary-bytes" {
return speedtest.UnitTypeBinaryBytes
} else {
return speedtest.UnitTypeDefaultMbps
}
}

func parseProto(str string) speedtest.Proto {
str = strings.ToLower(str)
if str == "icmp" {
Expand Down
Loading
Loading