1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package sync
import (
"fmt"
"net"
"sort"
"sync"
"time"
"vpnem/internal/models"
)
// LatencyResult holds a server's latency measurement.
type LatencyResult struct {
Tag string `json:"tag"`
Region string `json:"region"`
Latency int `json:"latency_ms"` // -1 means unreachable
}
// MeasureLatency pings all servers concurrently and returns results sorted by latency.
func MeasureLatency(servers []models.Server, timeout time.Duration) []LatencyResult {
var wg sync.WaitGroup
results := make([]LatencyResult, len(servers))
for i, s := range servers {
wg.Add(1)
go func(idx int, srv models.Server) {
defer wg.Done()
ms := tcpPing(srv.Server, srv.ServerPort, timeout)
results[idx] = LatencyResult{
Tag: srv.Tag,
Region: srv.Region,
Latency: ms,
}
}(i, s)
}
wg.Wait()
sort.Slice(results, func(i, j int) bool {
if results[i].Latency == -1 {
return false
}
if results[j].Latency == -1 {
return true
}
return results[i].Latency < results[j].Latency
})
return results
}
func tcpPing(host string, port int, timeout time.Duration) int {
addr := fmt.Sprintf("%s:%d", host, port)
start := time.Now()
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
return -1
}
conn.Close()
return int(time.Since(start).Milliseconds())
}
|