package main import ( "encoding/json" "flag" "fmt" "log" "net" "os" "strings" "sync" "time" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" ) // ============ 通用数据结构 ============ type TestPacket struct { SeqNum uint64 Timestamp int64 Type string // "tcp", "udp", "ping" Data []byte } type Stats struct { PacketsReceived uint64 PacketsLost uint64 LastSeqNum uint64 RTTSamples []time.Duration } type Metrics struct { PacketsSent uint64 PacketsReceived uint64 PacketsLost uint64 RTTSamples []time.Duration MinRTT time.Duration MaxRTT time.Duration AvgRTT time.Duration Jitter time.Duration } type TestReport struct { Timestamp time.Time TestDuration time.Duration TargetHost string TCPMetrics *Metrics UDPMetrics *Metrics TracerouteHops []HopInfo } type HopInfo struct { TTL int Address string RTT time.Duration } // ============ 服务端实现 ============ type NetworkServer struct { tcpAddr string udpAddr string tcpStats *Stats udpStats *Stats statsLock sync.RWMutex } func NewNetworkServer(tcpPort, udpPort int) *NetworkServer { return &NetworkServer{ tcpAddr: fmt.Sprintf(":%d", tcpPort), udpAddr: fmt.Sprintf(":%d", udpPort), tcpStats: &Stats{}, udpStats: &Stats{}, } } func (ns *NetworkServer) Start() { log.Printf("========== 网络质量检测服务端 ==========") log.Printf("TCP监听端口: %s", ns.tcpAddr) log.Printf("UDP监听端口: %s", ns.udpAddr) log.Printf("服务器已启动,等待客户端连接...") log.Printf("========================================\n") // 启动TCP服务器 go ns.serveTCP() // 启动UDP服务器 go ns.serveUDP() select {} } func (ns *NetworkServer) serveTCP() { listener, err := net.Listen("tcp", ns.tcpAddr) if err != nil { log.Fatalf("TCP监听失败: %v", err) } defer listener.Close() for { conn, err := listener.Accept() if err != nil { log.Printf("TCP连接接受错误: %v", err) continue } go ns.handleTCPConnection(conn) } } func (ns *NetworkServer) handleTCPConnection(conn net.Conn) { defer conn.Close() log.Printf("[TCP] 新连接来自 %s", conn.RemoteAddr()) buf := make([]byte, 8192) for { n, err := conn.Read(buf) if err != nil { log.Printf("[TCP] 连接 %s 断开", conn.RemoteAddr()) return } var packet TestPacket if err := json.Unmarshal(buf[:n], &packet); err != nil { continue } receiveTime := time.Now().UnixNano() ns.updateStats(ns.tcpStats, packet.SeqNum) // 立即回显数据包 response := TestPacket{ SeqNum: packet.SeqNum, Timestamp: receiveTime, Type: "tcp_response", Data: packet.Data, } data, _ := json.Marshal(response) conn.Write(data) } } func (ns *NetworkServer) serveUDP() { addr, err := net.ResolveUDPAddr("udp", ns.udpAddr) if err != nil { log.Fatalf("UDP地址解析失败: %v", err) } conn, err := net.ListenUDP("udp", addr) if err != nil { log.Fatalf("UDP监听失败: %v", err) } defer conn.Close() log.Printf("[UDP] 监听在 %s", ns.udpAddr) buf := make([]byte, 8192) for { n, remoteAddr, err := conn.ReadFromUDP(buf) if err != nil { log.Printf("UDP读取错误: %v", err) continue } var packet TestPacket if err := json.Unmarshal(buf[:n], &packet); err != nil { continue } receiveTime := time.Now().UnixNano() ns.updateStats(ns.udpStats, packet.SeqNum) // 回显UDP数据包 response := TestPacket{ SeqNum: packet.SeqNum, Timestamp: receiveTime, Type: "udp_response", Data: packet.Data, } data, _ := json.Marshal(response) conn.WriteToUDP(data, remoteAddr) } } func (ns *NetworkServer) updateStats(stats *Stats, seqNum uint64) { ns.statsLock.Lock() defer ns.statsLock.Unlock() stats.PacketsReceived++ if seqNum > stats.LastSeqNum+1 { stats.PacketsLost += seqNum - stats.LastSeqNum - 1 } stats.LastSeqNum = seqNum } // ============ 客户端实现 ============ type NetworkClient struct { targetHost string tcpPort int udpPort int testDuration time.Duration packetSize int reportFile string tcpMetrics *Metrics udpMetrics *Metrics mu sync.Mutex } func NewNetworkClient(host string, tcpPort, udpPort int, duration time.Duration) *NetworkClient { return &NetworkClient{ targetHost: host, tcpPort: tcpPort, udpPort: udpPort, testDuration: duration, packetSize: 1024, reportFile: "network_quality_report.json", tcpMetrics: &Metrics{MinRTT: time.Hour}, udpMetrics: &Metrics{MinRTT: time.Hour}, } } func (nc *NetworkClient) testTCPLatency() error { addr := fmt.Sprintf("%s:%d", nc.targetHost, nc.tcpPort) conn, err := net.DialTimeout("tcp", addr, 5*time.Second) if err != nil { return fmt.Errorf("TCP连接失败: %v", err) } defer conn.Close() log.Printf("[TCP] 开始延迟测试 -> %s", addr) var seqNum uint64 deadline := time.Now().Add(nc.testDuration) for time.Now().Before(deadline) { seqNum++ packet := TestPacket{ SeqNum: seqNum, Timestamp: time.Now().UnixNano(), Type: "tcp_probe", Data: make([]byte, nc.packetSize), } sendTime := time.Now() data, _ := json.Marshal(packet) if _, err := conn.Write(data); err != nil { nc.mu.Lock() nc.tcpMetrics.PacketsLost++ nc.mu.Unlock() continue } nc.mu.Lock() nc.tcpMetrics.PacketsSent++ nc.mu.Unlock() buf := make([]byte, 8192) conn.SetReadDeadline(time.Now().Add(2 * time.Second)) _, err := conn.Read(buf) if err != nil { nc.mu.Lock() nc.tcpMetrics.PacketsLost++ nc.mu.Unlock() continue } rtt := time.Since(sendTime) nc.updateTCPMetrics(rtt) time.Sleep(100 * time.Millisecond) } log.Printf("[TCP] 测试完成") return nil } func (nc *NetworkClient) testUDPLatency() error { addr := fmt.Sprintf("%s:%d", nc.targetHost, nc.udpPort) raddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return fmt.Errorf("UDP地址解析失败: %v", err) } conn, err := net.DialUDP("udp", nil, raddr) if err != nil { return fmt.Errorf("UDP连接失败: %v", err) } defer conn.Close() log.Printf("[UDP] 开始延迟测试 -> %s", addr) var seqNum uint64 deadline := time.Now().Add(nc.testDuration) sentPackets := make(map[uint64]time.Time) var wg sync.WaitGroup // 发送协程 wg.Add(1) go func() { defer wg.Done() for time.Now().Before(deadline) { seqNum++ packet := TestPacket{ SeqNum: seqNum, Timestamp: time.Now().UnixNano(), Type: "udp_probe", Data: make([]byte, nc.packetSize), } sendTime := time.Now() nc.mu.Lock() sentPackets[seqNum] = sendTime nc.mu.Unlock() data, _ := json.Marshal(packet) conn.Write(data) nc.mu.Lock() nc.udpMetrics.PacketsSent++ nc.mu.Unlock() time.Sleep(100 * time.Millisecond) } }() // 接收协程 buf := make([]byte, 8192) for time.Now().Before(deadline.Add(3 * time.Second)) { conn.SetReadDeadline(time.Now().Add(1 * time.Second)) n, err := conn.Read(buf) if err != nil { continue } var response TestPacket if err := json.Unmarshal(buf[:n], &response); err != nil { continue } nc.mu.Lock() if sendTime, ok := sentPackets[response.SeqNum]; ok { rtt := time.Since(sendTime) nc.updateUDPMetrics(rtt) delete(sentPackets, response.SeqNum) } nc.mu.Unlock() } wg.Wait() nc.mu.Lock() nc.udpMetrics.PacketsLost = uint64(len(sentPackets)) nc.mu.Unlock() log.Printf("[UDP] 测试完成") return nil } func (nc *NetworkClient) performTraceroute() ([]HopInfo, error) { log.Printf("[Traceroute] 路由追踪到 %s", nc.targetHost) hops := make([]HopInfo, 0, 30) maxTTL := 30 timeout := 2 * time.Second for ttl := 1; ttl <= maxTTL; ttl++ { hopInfo, reached, err := nc.probeHop(ttl, timeout) if err != nil { continue } hops = append(hops, hopInfo) if hopInfo.Address != "*" { log.Printf(" %2d %-15s %.2fms", ttl, hopInfo.Address, float64(hopInfo.RTT.Microseconds())/1000.0) } else { log.Printf(" %2d *", ttl) } if reached { break } } return hops, nil } func (nc *NetworkClient) probeHop(ttl int, timeout time.Duration) (HopInfo, bool, error) { conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { return HopInfo{}, false, err } defer conn.Close() if err := conn.IPv4PacketConn().SetTTL(ttl); err != nil { return HopInfo{}, false, err } msg := icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: os.Getpid() & 0xffff, Seq: ttl, Data: []byte("TRACEROUTE"), }, } msgBytes, err := msg.Marshal(nil) if err != nil { return HopInfo{}, false, err } dst, err := net.ResolveIPAddr("ip4", nc.targetHost) if err != nil { return HopInfo{}, false, err } start := time.Now() if _, err := conn.WriteTo(msgBytes, dst); err != nil { return HopInfo{}, false, err } reply := make([]byte, 1500) conn.SetReadDeadline(time.Now().Add(timeout)) _, peer, err := conn.ReadFrom(reply) rtt := time.Since(start) if err != nil { return HopInfo{TTL: ttl, Address: "*", RTT: 0}, false, nil } hopAddr := peer.String() reachedTarget := (hopAddr == dst.String()) return HopInfo{ TTL: ttl, Address: hopAddr, RTT: rtt, }, reachedTarget, nil } func (nc *NetworkClient) updateTCPMetrics(rtt time.Duration) { nc.mu.Lock() defer nc.mu.Unlock() nc.tcpMetrics.PacketsReceived++ nc.tcpMetrics.RTTSamples = append(nc.tcpMetrics.RTTSamples, rtt) if rtt < nc.tcpMetrics.MinRTT { nc.tcpMetrics.MinRTT = rtt } if rtt > nc.tcpMetrics.MaxRTT { nc.tcpMetrics.MaxRTT = rtt } } func (nc *NetworkClient) updateUDPMetrics(rtt time.Duration) { nc.udpMetrics.PacketsReceived++ nc.udpMetrics.RTTSamples = append(nc.udpMetrics.RTTSamples, rtt) if rtt < nc.udpMetrics.MinRTT { nc.udpMetrics.MinRTT = rtt } if rtt > nc.udpMetrics.MaxRTT { nc.udpMetrics.MaxRTT = rtt } } func (nc *NetworkClient) calculateMetrics() { // 计算TCP平均RTT和抖动 if len(nc.tcpMetrics.RTTSamples) > 0 { var sum time.Duration for _, rtt := range nc.tcpMetrics.RTTSamples { sum += rtt } nc.tcpMetrics.AvgRTT = sum / time.Duration(len(nc.tcpMetrics.RTTSamples)) var jitterSum time.Duration for i := 1; i < len(nc.tcpMetrics.RTTSamples); i++ { diff := nc.tcpMetrics.RTTSamples[i] - nc.tcpMetrics.RTTSamples[i-1] if diff < 0 { diff = -diff } jitterSum += diff } if len(nc.tcpMetrics.RTTSamples) > 1 { nc.tcpMetrics.Jitter = jitterSum / time.Duration(len(nc.tcpMetrics.RTTSamples)-1) } } // 计算UDP平均RTT和抖动 if len(nc.udpMetrics.RTTSamples) > 0 { var sum time.Duration for _, rtt := range nc.udpMetrics.RTTSamples { sum += rtt } nc.udpMetrics.AvgRTT = sum / time.Duration(len(nc.udpMetrics.RTTSamples)) var jitterSum time.Duration for i := 1; i < len(nc.udpMetrics.RTTSamples); i++ { diff := nc.udpMetrics.RTTSamples[i] - nc.udpMetrics.RTTSamples[i-1] if diff < 0 { diff = -diff } jitterSum += diff } if len(nc.udpMetrics.RTTSamples) > 1 { nc.udpMetrics.Jitter = jitterSum / time.Duration(len(nc.udpMetrics.RTTSamples)-1) } } } func (nc *NetworkClient) generateReport(hops []HopInfo) error { nc.calculateMetrics() report := TestReport{ Timestamp: time.Now(), TestDuration: nc.testDuration, TargetHost: nc.targetHost, TCPMetrics: nc.tcpMetrics, UDPMetrics: nc.udpMetrics, TracerouteHops: hops, } // 将报告序列化为JSON(单行,不格式化) data, err := json.Marshal(report) if err != nil { return err } // 以追加模式打开文件,如果不存在则创建 file, err := os.OpenFile(nc.reportFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return fmt.Errorf("打开报告文件失败: %v", err) } defer file.Close() // 写入JSON数据并添加换行符(JSON Lines格式) if _, err := file.Write(data); err != nil { return fmt.Errorf("写入报告数据失败: %v", err) } if _, err := file.WriteString("\n"); err != nil { return fmt.Errorf("写入换行符失败: %v", err) } log.Printf("测试报告已追加至: %s", nc.reportFile) nc.printReport(&report) return nil } func (nc *NetworkClient) printReport(report *TestReport) { fmt.Println("\n" + strings.Repeat("=", 50)) fmt.Println(" 网络质量检测报告") fmt.Println(strings.Repeat("=", 50)) fmt.Printf("测试时间: %s\n", report.Timestamp.Format("2006-01-02 15:04:05")) fmt.Printf("目标主机: %s\n", report.TargetHost) fmt.Printf("测试时长: %v\n", report.TestDuration) fmt.Println(strings.Repeat("-", 50)) fmt.Println("\n【TCP 测试结果】") fmt.Printf(" 发送包数: %d\n", report.TCPMetrics.PacketsSent) fmt.Printf(" 接收包数: %d\n", report.TCPMetrics.PacketsReceived) if report.TCPMetrics.PacketsSent > 0 { lossRate := float64(report.TCPMetrics.PacketsLost) / float64(report.TCPMetrics.PacketsSent) * 100 fmt.Printf(" 丢包数量: %d (丢包率: %.2f%%)\n", report.TCPMetrics.PacketsLost, lossRate) } if report.TCPMetrics.MinRTT < time.Hour { fmt.Printf(" 最小RTT: %v\n", report.TCPMetrics.MinRTT) fmt.Printf(" 平均RTT: %v\n", report.TCPMetrics.AvgRTT) fmt.Printf(" 最大RTT: %v\n", report.TCPMetrics.MaxRTT) fmt.Printf(" 抖动: %v\n", report.TCPMetrics.Jitter) } fmt.Println("\n【UDP 测试结果】") fmt.Printf(" 发送包数: %d\n", report.UDPMetrics.PacketsSent) fmt.Printf(" 接收包数: %d\n", report.UDPMetrics.PacketsReceived) if report.UDPMetrics.PacketsSent > 0 { lossRate := float64(report.UDPMetrics.PacketsLost) / float64(report.UDPMetrics.PacketsSent) * 100 fmt.Printf(" 丢包数量: %d (丢包率: %.2f%%)\n", report.UDPMetrics.PacketsLost, lossRate) } if report.UDPMetrics.MinRTT < time.Hour { fmt.Printf(" 最小RTT: %v\n", report.UDPMetrics.MinRTT) fmt.Printf(" 平均RTT: %v\n", report.UDPMetrics.AvgRTT) fmt.Printf(" 最大RTT: %v\n", report.UDPMetrics.MaxRTT) fmt.Printf(" 抖动: %v\n", report.UDPMetrics.Jitter) } fmt.Println(strings.Repeat("=", 50)) fmt.Printf("报告已保存至: %s\n\n", nc.reportFile) } func (nc *NetworkClient) RunScheduledTests(interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { log.Printf("\n========== 开始定时测试 [%s] ==========", time.Now().Format("2006-01-02 15:04:05")) // 执行TCP测试 if err := nc.testTCPLatency(); err != nil { log.Printf("TCP测试错误: %v", err) } // 重置UDP指标 nc.mu.Lock() nc.udpMetrics = &Metrics{MinRTT: time.Hour} nc.mu.Unlock() // 执行UDP测试 if err := nc.testUDPLatency(); err != nil { log.Printf("UDP测试错误: %v", err) } // 执行Traceroute hops, err := nc.performTraceroute() if err != nil { log.Printf("Traceroute错误: %v", err) } // 生成报告 if err := nc.generateReport(hops); err != nil { log.Printf("报告生成错误: %v", err) } // 重置TCP指标准备下一轮 nc.mu.Lock() nc.tcpMetrics = &Metrics{MinRTT: time.Hour} nc.mu.Unlock() log.Printf("========== 测试完成,等待下一轮 ==========\n") <-ticker.C } } // ============ 主程序 ============ func main() { // 定义命令行参数 var ( mode = flag.String("mode", "", "运行模式: server(服务端) 或 client(客户端)") tcpPort = flag.Int("tcp", 9001, "TCP测试端口") udpPort = flag.Int("udp", 9002, "UDP测试端口") targetHost = flag.String("target", "", "目标主机地址(客户端模式必需)") testDuration = flag.Int("duration", 60, "单次测试时长(秒)") interval = flag.Int("interval", 3600, "定时测试间隔(秒), 0表示只执行一次") ) flag.Usage = func() { fmt.Fprintf(os.Stderr, "\n网络质量检测工具\n\n") fmt.Fprintf(os.Stderr, "用法:\n") fmt.Fprintf(os.Stderr, " 服务端模式: %s -mode server [-tcp 端口] [-udp 端口]\n", os.Args[0]) fmt.Fprintf(os.Stderr, " 客户端模式: %s -mode client -target 目标IP [-tcp 端口] [-udp 端口] [-duration 秒] [-interval 秒]\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, "参数说明:\n") flag.PrintDefaults() fmt.Fprintf(os.Stderr, "\n示例:\n") fmt.Fprintf(os.Stderr, " # 启动服务端,使用默认端口\n") fmt.Fprintf(os.Stderr, " %s -mode server\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, " # 启动服务端,自定义端口\n") fmt.Fprintf(os.Stderr, " %s -mode server -tcp 8001 -udp 8002\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, " # 启动客户端,单次测试60秒\n") fmt.Fprintf(os.Stderr, " %s -mode client -target 192.168.1.100 -duration 60 -interval 0\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, " # 启动客户端,每小时测试一次\n") fmt.Fprintf(os.Stderr, " %s -mode client -target 192.168.1.100 -interval 3600\n\n", os.Args[0]) } flag.Parse() // 验证参数 if *mode == "" { fmt.Fprintf(os.Stderr, "错误: 必须指定运行模式 -mode server 或 -mode client\n\n") flag.Usage() os.Exit(1) } switch *mode { case "server": // 服务端模式 server := NewNetworkServer(*tcpPort, *udpPort) server.Start() case "client": // 客户端模式 if *targetHost == "" { fmt.Fprintf(os.Stderr, "错误: 客户端模式必须指定 -target 参数\n\n") flag.Usage() os.Exit(1) } client := NewNetworkClient(*targetHost, *tcpPort, *udpPort, time.Duration(*testDuration)*time.Second) if *interval == 0 { // 单次执行 log.Printf("开始单次网络质量测试...") if err := client.testTCPLatency(); err != nil { log.Printf("TCP测试错误: %v", err) } if err := client.testUDPLatency(); err != nil { log.Printf("UDP测试错误: %v", err) } hops, err := client.performTraceroute() if err != nil { log.Printf("Traceroute错误: %v", err) } if err := client.generateReport(hops); err != nil { log.Printf("报告生成错误: %v", err) } } else { // 定时执行 log.Printf("开始定时网络质量监控,间隔: %d秒", *interval) client.RunScheduledTests(time.Duration(*interval) * time.Second) } default: fmt.Fprintf(os.Stderr, "错误: 无效的运行模式 '%s',必须是 'server' 或 'client'\n\n", *mode) flag.Usage() os.Exit(1) } }