You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

159 lines
3.3 KiB

package localchat
import (
"encoding/json"
"fmt"
"log"
"net"
"net/http"
"runtime"
"sync"
"time"
"godo/libs"
)
type UserStatus struct {
Hostname string `json:"hostname"`
IP string `json:"ip"`
Time time.Time `json:"time"`
}
var OnlineUsers = make(map[string]UserStatus)
type UDPPayload struct {
Action string `json:"action"`
Data string `json:"data"`
}
func getHostname(ip string) (string, error) {
hostname, err := net.LookupAddr(ip)
if err != nil {
return "", fmt.Errorf("error getting hostname: %v", err)
}
if len(hostname) > 0 {
return hostname[0], nil
}
return "", fmt.Errorf("no hostname found for IP: %s", ip)
}
// 发送 UDP 包并忽略响应
func sendUDPPacket(ip string) error {
payload := UDPPayload{
Action: "check",
Data: "",
}
log.Printf("sending ip: %+v", ip)
payloadBytes, err := json.Marshal(payload)
if err != nil {
log.Printf("error marshalling payload: %v", err)
return fmt.Errorf("error marshalling payload: %v", err)
}
conn, err := net.Dial("udp", fmt.Sprintf("%s:56780", ip))
if err != nil {
log.Printf("error dialing UDP: %v", err)
return fmt.Errorf("error dialing UDP: %v", err)
}
defer conn.Close()
_, err = conn.Write(payloadBytes)
if err != nil {
log.Printf("error writing UDP payload: %v", err)
return fmt.Errorf("error writing UDP payload: %v", err)
}
return nil
}
func concurrentGetIpInfo(ips []string) {
// 获取本地 IP 地址
hostips, err := libs.GetValidIPAddresses()
if err != nil {
log.Printf("failed to get local IP addresses: %v", err)
return
}
var wg sync.WaitGroup
maxConcurrency := runtime.NumCPU()
semaphore := make(chan struct{}, maxConcurrency)
failedIPs := make(map[string]bool)
for _, ip := range ips {
if containArr(hostips, ip) || failedIPs[ip] {
continue
}
wg.Add(1)
semaphore <- struct{}{}
go func(ip string) {
defer wg.Done()
defer func() { <-semaphore }()
err := sendUDPPacket(ip)
if err != nil {
log.Printf("Failed to send packet to IP %s: %v", ip, err)
failedIPs[ip] = true // 标记失败的 IP
} else {
hostname, err := getHostname(ip)
if err != nil {
log.Printf("Failed to get hostname for IP %s: %v", ip, err)
} else {
OnlineUsers[ip] = UserStatus{
Hostname: hostname,
IP: ip,
Time: time.Now(),
}
}
}
}(ip)
}
wg.Wait()
}
func CheckOnline() {
// 清除 OnlineUsers 映射表
CleanOnlineUsers()
ips := libs.GetChatIPs()
// 启动并发处理
concurrentGetIpInfo(ips)
log.Printf("online users: %v", OnlineUsers)
}
func CleanOnlineUsers() {
OnlineUsers = make(map[string]UserStatus)
}
func containArr(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}
func HandleHeartbeat(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr // 可以根据实际情况获取 IP
hostname, err := getHostname(ip)
if err != nil {
libs.HTTPError(w, http.StatusInternalServerError, "Failed to get hostname")
return
}
userStatus := UpdateUserStatus(ip, hostname)
libs.SuccessMsg(w, userStatus, "Heartbeat received")
}
func UpdateUserStatus(ip string, hostname string) UserStatus {
OnlineUsers[ip] = UserStatus{
Hostname: hostname,
IP: ip,
Time: time.Now(),
}
return OnlineUsers[ip]
}