164 lines
3.4 KiB
Go
164 lines
3.4 KiB
Go
package handler
|
||
|
||
import (
|
||
"net"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
func handleTCPing(c *gin.Context, url string, params map[string]interface{}) {
|
||
// 获取seq参数
|
||
seq := ""
|
||
if seqVal, ok := params["seq"].(string); ok {
|
||
seq = seqVal
|
||
}
|
||
|
||
// 解析host:port格式
|
||
parts := strings.Split(url, ":")
|
||
if len(parts) != 2 {
|
||
c.JSON(200, gin.H{
|
||
"seq": seq,
|
||
"type": "ceTCPing",
|
||
"url": url,
|
||
"error": "格式错误,需要 host:port",
|
||
})
|
||
return
|
||
}
|
||
|
||
host := parts[0]
|
||
portStr := parts[1]
|
||
port, err := strconv.Atoi(portStr)
|
||
if err != nil {
|
||
c.JSON(200, gin.H{
|
||
"seq": seq,
|
||
"type": "ceTCPing",
|
||
"url": url,
|
||
"error": "端口格式错误",
|
||
})
|
||
return
|
||
}
|
||
|
||
// 解析hostname获取IP
|
||
var primaryIP string
|
||
ips, err := net.LookupIP(host)
|
||
if err == nil && len(ips) > 0 {
|
||
// 优先使用IPv4
|
||
for _, ip := range ips {
|
||
if ip.To4() != nil {
|
||
primaryIP = ip.String()
|
||
break
|
||
}
|
||
}
|
||
if primaryIP == "" && len(ips) > 0 {
|
||
primaryIP = ips[0].String()
|
||
}
|
||
}
|
||
|
||
// 执行多次TCP连接测试(默认10次,和PING一致)
|
||
const testCount = 10
|
||
var latencies []float64
|
||
successCount := 0
|
||
failureCount := 0
|
||
|
||
for i := 0; i < testCount; i++ {
|
||
start := time.Now()
|
||
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, portStr), 5*time.Second)
|
||
latency := time.Since(start).Milliseconds()
|
||
|
||
if err == nil {
|
||
// 成功:记录延迟
|
||
latencies = append(latencies, float64(latency))
|
||
successCount++
|
||
conn.Close()
|
||
|
||
// 如果之前没有获取到IP,从连接中获取
|
||
if primaryIP == "" {
|
||
if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
||
primaryIP = addr.IP.String()
|
||
}
|
||
}
|
||
} else {
|
||
// 失败:记录为丢包
|
||
failureCount++
|
||
}
|
||
}
|
||
|
||
// 计算统计信息
|
||
packetsTotal := testCount
|
||
packetsRecv := successCount
|
||
packetsLosrat := float64(failureCount) / float64(testCount) * 100.0
|
||
|
||
var timeMin, timeMax, timeAvg float64
|
||
if len(latencies) > 0 {
|
||
timeMin = latencies[0]
|
||
timeMax = latencies[0]
|
||
sum := 0.0
|
||
for _, lat := range latencies {
|
||
if lat < timeMin {
|
||
timeMin = lat
|
||
}
|
||
if lat > timeMax {
|
||
timeMax = lat
|
||
}
|
||
sum += lat
|
||
}
|
||
timeAvg = sum / float64(len(latencies))
|
||
} else {
|
||
// 全部失败
|
||
timeMin = -1
|
||
timeMax = -1
|
||
timeAvg = -1
|
||
}
|
||
|
||
// 如果之前没有获取到IP,尝试从host解析
|
||
if primaryIP == "" {
|
||
ips, err := net.LookupIP(host)
|
||
if err == nil && len(ips) > 0 {
|
||
for _, ip := range ips {
|
||
if ip.To4() != nil {
|
||
primaryIP = ip.String()
|
||
break
|
||
}
|
||
}
|
||
if primaryIP == "" && len(ips) > 0 {
|
||
primaryIP = ips[0].String()
|
||
}
|
||
}
|
||
}
|
||
|
||
// 返回格式和PING一致
|
||
result := gin.H{
|
||
"seq": seq,
|
||
"type": "ceTCPing",
|
||
"url": url,
|
||
"ip": primaryIP,
|
||
"host": host,
|
||
"port": port,
|
||
"packets_total": strconv.Itoa(packetsTotal),
|
||
"packets_recv": strconv.Itoa(packetsRecv),
|
||
"packets_losrat": packetsLosrat, // float64类型,百分比值(如10.5表示10.5%)
|
||
}
|
||
|
||
// 时间字段:如果是-1(全部失败),返回字符串"-",否则返回float64
|
||
if timeMin < 0 {
|
||
result["time_min"] = "-"
|
||
result["time_max"] = "-"
|
||
result["time_avg"] = "-"
|
||
} else {
|
||
result["time_min"] = timeMin
|
||
result["time_max"] = timeMax
|
||
result["time_avg"] = timeAvg
|
||
}
|
||
|
||
// 如果全部失败,添加error字段
|
||
if successCount == 0 {
|
||
result["error"] = "所有TCP连接测试均失败"
|
||
}
|
||
|
||
c.JSON(200, result)
|
||
}
|
||
|