plugai_updsrv/pkg/utils/network.go

146 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package utils
import (
"context"
"encoding/json"
"fmt"
"net"
"os/exec"
"runtime"
"strings"
"time"
"github.com/civet148/log"
)
/* ---------- 获取本机同网段 IP ---------- */
func getGatewayIP() (string, error) {
var cmd *exec.Cmd
switch runtime.GOOS {
case "linux":
cmd = exec.Command("sh", "-c", "ip route | grep default | awk '{print $3}'")
case "darwin":
cmd = exec.Command("sh", "-c", "netstat -rn | grep default | awk '{print $2}'")
default:
return "", fmt.Errorf("unsupported OS")
}
out, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(out)), nil
}
func ipInSameSubnet(ip1 net.IP, ip2 net.IP, mask net.IPMask) bool {
if mask == nil {
return false
}
return ip1.Mask(mask).Equal(ip2.Mask(mask))
}
func GetLocalIPSameAsGW() string {
gwStr, err := getGatewayIP()
if err != nil {
return "127.0.0.1"
}
gw := net.ParseIP(gwStr)
ifaces, _ := net.Interfaces()
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue
}
addrs, _ := iface.Addrs()
for _, a := range addrs {
var ip net.IP
var mask net.IPMask
switch v := a.(type) {
case *net.IPNet:
ip = v.IP
mask = v.Mask
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() || ip.To4() == nil {
continue
}
if ipInSameSubnet(ip, gw, mask) {
return ip.String()
}
}
}
return "127.0.0.1"
}
func IsValidIP(ip string) bool {
if ip == "" || ip == "127.0.0.1" {
return false
}
p := net.ParseIP(ip)
return p != nil && !p.IsLoopback() && !p.IsUnspecified() &&
!p.IsLinkLocalMulticast() && !p.IsLinkLocalUnicast()
}
/* ---------- UDP 广播 ---------- */
type BroadcastMessage struct {
Type string `json:"type"`
IP string `json:"ip"`
Port int `json:"port"`
Name string `json:"name"`
}
// StartBroadcast 启动 UDP 广播ctx 取消时自动退出
func StartBroadcast(ctx context.Context, port, interval int, name string) {
go func() {
var prevIP string
var conn *net.UDPConn
for {
select {
case <-ctx.Done():
if conn != nil {
conn.Close()
}
return
default:
}
curIP := GetLocalIPSameAsGW()
if !IsValidIP(curIP) {
time.Sleep(time.Duration(interval) * time.Second)
continue
}
if curIP != prevIP || conn == nil {
if conn != nil {
conn.Close()
}
addr := net.UDPAddr{IP: net.IPv4bcast, Port: port}
c, err := net.DialUDP("udp4", nil, &addr)
if err != nil {
log.Warnf("UDP 连接失败: %v", err)
time.Sleep(time.Duration(interval) * time.Second)
continue
}
conn = c
prevIP = curIP
log.Infof("绑定新 IP 广播: %s", curIP)
}
msg := BroadcastMessage{
Type: "ai_server_announce",
IP: curIP,
Port: port,
Name: name,
}
data, _ := json.Marshal(msg)
if _, err := conn.Write(data); err != nil {
log.Warnf("广播失败: %v", err)
}
time.Sleep(time.Duration(interval) * time.Second)
}
}()
}