first commit!

This commit is contained in:
hailin 2025-06-12 06:23:16 +00:00
commit 43f61be8b8
5 changed files with 218 additions and 0 deletions

2
linux-desktop/build.sh Normal file
View File

@ -0,0 +1,2 @@
pip install pyinstaller
pyinstaller --onefile src/linux_udp_client.py

View File

@ -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()

BIN
server/broadcaster Executable file

Binary file not shown.

140
server/broadcaster.go Normal file
View File

@ -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)
}
}

4
server/build.sh Normal file
View File

@ -0,0 +1,4 @@
# Linux/macOS
go build -o broadcaster broadcaster.go
./broadcaster -port=9876 -interval=2 -name="T1-GRT309024X2"