first commit!
This commit is contained in:
commit
43f61be8b8
|
|
@ -0,0 +1,2 @@
|
||||||
|
pip install pyinstaller
|
||||||
|
pyinstaller --onefile 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()
|
||||||
|
|
||||||
Binary file not shown.
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Linux/macOS
|
||||||
|
go build -o broadcaster broadcaster.go
|
||||||
|
./broadcaster -port=9876 -interval=2 -name="T1-GRT309024X2"
|
||||||
|
|
||||||
Loading…
Reference in New Issue