commit 43f61be8b8391ba0ed963812ae731819fafb27aa Author: hailin Date: Thu Jun 12 06:23:16 2025 +0000 first commit! diff --git a/linux-desktop/build.sh b/linux-desktop/build.sh new file mode 100644 index 0000000..5233667 --- /dev/null +++ b/linux-desktop/build.sh @@ -0,0 +1,2 @@ +pip install pyinstaller +pyinstaller --onefile src/linux_udp_client.py diff --git a/linux-desktop/src/linux_udp_client.py b/linux-desktop/src/linux_udp_client.py new file mode 100644 index 0000000..942e8e8 --- /dev/null +++ b/linux-desktop/src/linux_udp_client.py @@ -0,0 +1,72 @@ +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, GLib + +import socket +import json +import threading +import webbrowser + +UDP_PORT = 9876 + +class UDPClient(Gtk.Window): + def __init__(self): + super().__init__(title="AI服务器发现工具") + self.set_border_width(20) + self.set_default_size(400, 150) + + self.label = Gtk.Label(label="监听中... 请确保 AI 服务器在线") + self.add(self.label) + + self.popup_shown = False # 每次启动只弹一次 + + threading.Thread(target=self.listen_udp, daemon=True).start() + + def listen_udp(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind(("", UDP_PORT)) + print(f"Listening on UDP port {UDP_PORT}...") + + while True: + data, addr = sock.recvfrom(1024) + try: + msg = json.loads(data.decode("utf-8")) + if msg.get("type") == "ai_server_announce": + ip = msg.get("ip") + name = msg.get("name", "Unknown") + url = f"http://{ip}:80" # 明确指定80端口 + + if not self.popup_shown: + self.popup_shown = True + GLib.idle_add(self.update_ui, name, url) + + except Exception as e: + print("Error parsing message:", e) + + def update_ui(self, name, url): + self.label.set_text(f"发现设备:{name}\n{url}") + dialog = Gtk.MessageDialog( + transient_for=self, + flags=0, + message_type=Gtk.MessageType.QUESTION, + buttons=Gtk.ButtonsType.YES_NO, + text="是否访问 AI 服务器?", + ) + dialog.format_secondary_text(url) + response = dialog.run() + dialog.destroy() + + if response == Gtk.ResponseType.YES: + webbrowser.open(url) + + return False + +def main(): + app = UDPClient() + app.connect("destroy", Gtk.main_quit) + app.show_all() + Gtk.main() + +if __name__ == "__main__": + main() + diff --git a/server/broadcaster b/server/broadcaster new file mode 100755 index 0000000..28ced47 Binary files /dev/null and b/server/broadcaster differ diff --git a/server/broadcaster.go b/server/broadcaster.go new file mode 100644 index 0000000..ce1925d --- /dev/null +++ b/server/broadcaster.go @@ -0,0 +1,140 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "net" + "os" + "os/exec" + "runtime" + "strings" + "time" +) + +type BroadcastMessage struct { + Type string `json:"type"` + IP string `json:"ip"` + Port int `json:"port"` + Name string `json:"name"` +} + +func getGatewayIP() (string, error) { + var cmd *exec.Cmd + if runtime.GOOS == "linux" { + cmd = exec.Command("sh", "-c", "ip route | grep default | awk '{print $3}'") + } else if runtime.GOOS == "darwin" { + cmd = exec.Command("sh", "-c", "netstat -rn | grep default | awk '{print $2}'") + } else { + 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 + } + net1 := ip1.Mask(mask) + net2 := ip2.Mask(mask) + return net1.Equal(net2) +} + +func getLocalIP() string { + gatewayStr, err := getGatewayIP() + if err != nil { + fmt.Println("Failed to get gateway IP:", err) + return "127.0.0.1" + } + gateway := net.ParseIP(gatewayStr) + if gateway == nil { + fmt.Println("Invalid gateway IP") + return "127.0.0.1" + } + + interfaces, err := net.Interfaces() + if err != nil { + fmt.Println("Failed to get network interfaces:", err) + return "127.0.0.1" + } + + for _, iface := range interfaces { + if iface.Flags&net.FlagUp == 0 { + continue // interface down + } + addrs, err := iface.Addrs() + if err != nil { + continue + } + for _, addr := range addrs { + var ip net.IP + var mask net.IPMask + + switch v := addr.(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, gateway, mask) { + return ip.String() + } + } + } + return "127.0.0.1" +} + +func main() { + // 参数 + var ( + port = flag.Int("port", 8000, "Device service port") + interval = flag.Int("interval", 2, "Broadcast interval in seconds") + name = flag.String("name", "My-AI-Server", "Device name") + ) + flag.Parse() + + ip := getLocalIP() + if ip == "127.0.0.1" { + fmt.Println("Warning: failed to find suitable IP address, using loopback") + } + + addr := net.UDPAddr{ + IP: net.IPv4bcast, + Port: 9876, + } + conn, err := net.DialUDP("udp4", nil, &addr) + if err != nil { + fmt.Println("Failed to dial UDP broadcast:", err) + os.Exit(1) + } + defer conn.Close() + + for { + msg := BroadcastMessage{ + Type: "ai_server_announce", + IP: ip, + Port: *port, + Name: *name, + } + data, _ := json.Marshal(msg) + _, err := conn.Write(data) + if err != nil { + fmt.Println("Broadcast failed:", err) + } else { + fmt.Printf("Broadcasted: %s:%d (%s)\n", ip, *port, *name) + } + time.Sleep(time.Duration(*interval) * time.Second) + } +} + diff --git a/server/build.sh b/server/build.sh new file mode 100644 index 0000000..5d9c55f --- /dev/null +++ b/server/build.sh @@ -0,0 +1,4 @@ +# Linux/macOS +go build -o broadcaster broadcaster.go +./broadcaster -port=9876 -interval=2 -name="T1-GRT309024X2" +