From 4c1fc5351d24b6b9aed92822083fd4fd86f12478 Mon Sep 17 00:00:00 2001 From: godo Date: Fri, 6 Sep 2024 10:56:00 +0800 Subject: [PATCH] change check online --- godo/cmd/main.go | 4 +- godo/libs/chatip.go | 71 +++++++++++++++ godo/libs/dir.go | 16 ++-- godo/localchat/addr.go | 190 +++++++++++++++++++++++++-------------- godo/localchat/check.go | 27 ++++++ godo/localchat/file.go | 4 +- godo/localchat/send.go | 2 +- godo/localchat/server.go | 126 ++++++++++++++++---------- godo/localchat/udp.go | 141 ----------------------------- 9 files changed, 309 insertions(+), 272 deletions(-) create mode 100644 godo/libs/chatip.go create mode 100644 godo/localchat/check.go delete mode 100644 godo/localchat/udp.go diff --git a/godo/cmd/main.go b/godo/cmd/main.go index b179e11..c65dc5b 100644 --- a/godo/cmd/main.go +++ b/godo/cmd/main.go @@ -80,9 +80,9 @@ func OsStart() { localchatRouter := router.PathPrefix("/localchat").Subrouter() localchatRouter.HandleFunc("/message", localchat.HandleMessage).Methods(http.MethodPost) - localchatRouter.HandleFunc("/file", localchat.HandlerFile).Methods(http.MethodPost) + //localchatRouter.HandleFunc("/file", localchat.HandlerFile).Methods(http.MethodPost) localchatRouter.HandleFunc("/udpaddr", localchat.HandleAddr).Methods(http.MethodGet) - + localchatRouter.HandleFunc("/check", localchat.HandleCheck).Methods(http.MethodGet) // 注册 WebDAV 路由 webdavRouter := router.PathPrefix("/webdav").Subrouter() webdavRouter.HandleFunc("/read", webdav.HandleReadDir).Methods(http.MethodGet) diff --git a/godo/libs/chatip.go b/godo/libs/chatip.go new file mode 100644 index 0000000..94f9462 --- /dev/null +++ b/godo/libs/chatip.go @@ -0,0 +1,71 @@ +package libs + +import ( + "fmt" + "strconv" +) + +type UserChatIpSetting struct { + First string `json:"First"` + Second string `json:"Second"` + ThirdStart string `json:"ThirdStart"` + ThirdEnd string `json:"ThirdEnd"` + FourthStart string `json:"FourthStart"` + FourthEnd string `json:"FourthEnd"` +} + +func GetDefaultChatIpSetting() UserChatIpSetting { + return UserChatIpSetting{ + First: "192", + Second: "168", + ThirdStart: "1", + ThirdEnd: "1", + FourthStart: "2", + FourthEnd: "99", + } +} + +func GetChatIpSetting() UserChatIpSetting { + ips, ok := GetConfig("chatIpSetting") + if !ok { + return GetDefaultChatIpSetting() + } + return ips.(UserChatIpSetting) +} + +// GenerateIPs 生成 IP 地址列表 +func GenerateIPs(setting UserChatIpSetting) []string { + var IPs []string + + thirdStart, _ := strconv.Atoi(setting.ThirdStart) + thirdEnd, _ := strconv.Atoi(setting.ThirdEnd) + fourthStart, _ := strconv.Atoi(setting.FourthStart) + fourthEnd, _ := strconv.Atoi(setting.FourthEnd) + + if thirdStart == thirdEnd { + if fourthStart == fourthEnd { + // 第三位和第四位都相等,只生成一个 IP 地址 + IPs = append(IPs, fmt.Sprintf("%s.%s.%d.%d", setting.First, setting.Second, thirdStart, fourthStart)) + } else { + // 第三位相等,第四位不相等,生成第四位的所有 IP 地址 + for j := fourthStart; j <= fourthEnd; j++ { + IPs = append(IPs, fmt.Sprintf("%s.%s.%d.%d", setting.First, setting.Second, thirdStart, j)) + } + } + } else { + // 第三位不相等,生成第三位和第四位的所有组合 + for i := thirdStart; i <= thirdEnd; i++ { + for j := fourthStart; j <= fourthEnd; j++ { + IPs = append(IPs, fmt.Sprintf("%s.%s.%d.%d", setting.First, setting.Second, i, j)) + } + } + } + + return IPs +} + +// 示例函数 +func GetChatIPs() []string { + setting := GetChatIpSetting() + return GenerateIPs(setting) +} diff --git a/godo/libs/dir.go b/godo/libs/dir.go index 2dd2ed1..b2b5fc4 100644 --- a/godo/libs/dir.go +++ b/godo/libs/dir.go @@ -29,11 +29,11 @@ func InitServer() error { Value: info, } SetConfig(osInfo) - udpAddr := ReqBody{ - Name: "udpAddr", - Value: "224.0.0.251:20249", + ipSetting := ReqBody{ + Name: "chatIpSetting", + Value: GetDefaultChatIpSetting(), } - SetConfig(udpAddr) + SetConfig(ipSetting) } return nil @@ -56,13 +56,7 @@ func GetOsDir() (string, error) { } return osDir.(string), nil } -func GetUdpAddr() string { - udp, ok := GetConfig("udpAddr") - if !ok { - return "224.0.0.251:20249" - } - return udp.(string) -} + func GetAppDir() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { diff --git a/godo/localchat/addr.go b/godo/localchat/addr.go index c8119b8..54cc31f 100644 --- a/godo/localchat/addr.go +++ b/godo/localchat/addr.go @@ -1,103 +1,159 @@ package localchat import ( + "encoding/json" + "fmt" + "log" "net" "net/http" - "strconv" + "runtime" + "sync" + "time" "godo/libs" ) -func HandleAddr(w http.ResponseWriter, r *http.Request) { - addr := r.URL.Query().Get("addr") - if addr == "" { - libs.ErrorMsg(w, "addr is empty") - return - } +type UserStatus struct { + Hostname string `json:"hostname"` + IP string `json:"ip"` + Time time.Time `json:"time"` +} - udpAddr := libs.GetUdpAddr() - if addr == udpAddr { - libs.ErrorMsg(w, "addr is same as current addr") - return - } +var OnlineUsers = make(map[string]UserStatus) - // 检查是否为多播地址 - if !IsValidMulticastAddr(addr) { - libs.ErrorMsg(w, "addr is not a valid multicast address") - return - } - // 验证可访问性 - if IsMulticastAddrAccessible(addr) { - save := libs.ReqBody{ - Value: addr, - Name: "udpAddr", - } - libs.SetConfig(save) - libs.SuccessMsg(w, nil, "addr is a valid multicast address and accessible") - } else { - libs.ErrorMsg(w, "addr is a valid multicast address but not accessible") - } +type UDPPayload struct { + Action string `json:"action"` + Data string `json:"data"` } -// 检查是否为多播地址 -func IsValidMulticastAddr(addr string) bool { - host, port, err := net.SplitHostPort(addr) +func getHostname(ip string) (string, error) { + hostname, err := net.LookupAddr(ip) if err != nil { - return false + return "", fmt.Errorf("error getting hostname: %v", err) } - - ip := net.ParseIP(host) - if ip == nil || !isMulticastIP(ip) { - return false + if len(hostname) > 0 { + return hostname[0], nil } - - _, err = strconv.Atoi(port) - return err == nil + return "", fmt.Errorf("no hostname found for IP: %s", ip) } -// 检查 IP 是否为多播地址 -func isMulticastIP(ip net.IP) bool { - if ip.To4() != nil { - return ip[0]&0xF0 == 0xE0 // 检查 IPv4 多播地址范围 224.0.0.0 - 239.255.255.255 +// 发送 UDP 包并忽略响应 +func sendUDPPacket(ip string) error { + payload := UDPPayload{ + Action: "check", + Data: "", } - return ip[0]&0xF0 == 0xE0 // 检查 IPv6 多播地址范围 FF00::/8 -} - -// 验证多播地址的可访问性 -func IsMulticastAddrAccessible(addr string) bool { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return false - } - - udpAddr, err := net.ResolveUDPAddr("udp4", net.JoinHostPort(host, port)) + log.Printf("sending ip: %+v", ip) + payloadBytes, err := json.Marshal(payload) if err != nil { - return false + log.Printf("error marshalling payload: %v", err) + return fmt.Errorf("error marshalling payload: %v", err) } - conn, err := net.ListenMulticastUDP("udp4", nil, udpAddr) + conn, err := net.Dial("udp", fmt.Sprintf("%s:56780", ip)) if err != nil { - return false + log.Printf("error dialing UDP: %v", err) + return fmt.Errorf("error dialing UDP: %v", err) } defer conn.Close() - // 发送一条测试消息 - testMsg := []byte("Test message") - _, err = conn.WriteToUDP(testMsg, udpAddr) + _, err = conn.Write(payloadBytes) if err != nil { - return false + log.Printf("error writing UDP payload: %v", err) + return fmt.Errorf("error writing UDP payload: %v", err) } - // 接收一条测试消息 - buffer := make([]byte, 1024) - n, _, err := conn.ReadFromUDP(buffer) + return nil +} + +func concurrentGetIpInfo(ips []string) { + // 获取本地 IP 地址 + hostips, err := libs.GetValidIPAddresses() if err != nil { - return false + log.Printf("failed to get local IP addresses: %v", err) + return } - if n > 0 && string(buffer[:n]) == "Test message" { - return true + 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] +} diff --git a/godo/localchat/check.go b/godo/localchat/check.go new file mode 100644 index 0000000..d4fdec1 --- /dev/null +++ b/godo/localchat/check.go @@ -0,0 +1,27 @@ +package localchat + +import ( + "encoding/json" + "godo/libs" + "net/http" + "os" +) + +func HandleCheck(w http.ResponseWriter, r *http.Request) { + hostname, err := os.Hostname() + if err != nil { + libs.ErrorMsg(w, "HandleMessage error") + return + } + libs.SuccessMsg(w, hostname, "") +} +func HandleAddr(w http.ResponseWriter, r *http.Request) { + var ipStr libs.UserChatIpSetting + err := json.NewDecoder(r.Body).Decode(&ipStr) + if err != nil { + libs.HTTPError(w, http.StatusBadRequest, "Failed to parse request body") + return + } + libs.SetConfigByName("ChatIpSetting", ipStr) + libs.SuccessMsg(w, nil, "success") +} diff --git a/godo/localchat/file.go b/godo/localchat/file.go index 7349733..9f22558 100644 --- a/godo/localchat/file.go +++ b/godo/localchat/file.go @@ -132,7 +132,9 @@ func SendFile(file *os.File, numChunks int, toIp string, message UdpMessage) { } // 发送文件块 - addr, err := net.ResolveUDPAddr("udp4", toIp) + //addr, err := net.ResolveUDPAddr("udp4", toIp+":56780") + port := "56780" + addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) if err != nil { log.Fatalf("Failed to resolve UDP address: %v", err) } diff --git a/godo/localchat/send.go b/godo/localchat/send.go index 520373a..8e21c71 100644 --- a/godo/localchat/send.go +++ b/godo/localchat/send.go @@ -37,7 +37,7 @@ func HandleMessage(w http.ResponseWriter, r *http.Request) { // SendToIP 向指定的 IP 地址发送 UDP 消息 func SendToIP(message UdpMessage) error { toIp := message.IP - port := GetBroadcastPort() + port := "56780" addr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%s", toIp, port)) if err != nil { log.Printf("Failed to resolve UDP address %s:%s: %v", toIp, port, err) diff --git a/godo/localchat/server.go b/godo/localchat/server.go index ed34430..46a837b 100644 --- a/godo/localchat/server.go +++ b/godo/localchat/server.go @@ -4,68 +4,96 @@ import ( "encoding/json" "log" "net" - "os" "time" ) -func InitBroadcast() { - ticker := time.NewTicker(5 * time.Second) // 每 5 秒发送一次广播消息 - defer ticker.Stop() +type UdpMessage struct { + Hostname string `json:"hostname"` + Type string `json:"type"` + Time time.Time `json:"time"` + IP string `json:"ip"` + Message any `json:"message"` +} +type Messages struct { + Messages []UdpMessage `json:"messages"` +} +type UserMessage struct { + Messages map[string]*Messages `json:"messages"` + Onlines map[string]UserStatus `json:"onlines"` +} + +var UserMessages = make(map[string]*Messages) +func init() { + go UdpServer() + go CheckOnlines() +} +func CheckOnlines() { + CheckOnline() + ticker := time.NewTicker(60 * time.Second) + defer ticker.Stop() for range ticker.C { - hostname, err := os.Hostname() - if err != nil { - log.Printf("Failed to get hostname: %v", err) - continue - } - message := UdpMessage{ - Type: "online", - Hostname: hostname, - Message: "", - Time: time.Now(), - } - //发送多播消息 - broadcastAddr := GetBroadcastAddr() - err = SendBroadcast(broadcastAddr, message) - if err != nil { - log.Println("Failed to send broadcast message:", err) - } + // 检查客户端是否已断开连接 + CheckOnline() } } -func SendBroadcast(broadcastAddr string, message UdpMessage) error { - addr, err := net.ResolveUDPAddr("udp4", broadcastAddr) +// UDP 服务器端逻辑 +func UdpServer() { + // 监听 UDP 端口 + listener, err := net.ListenPacket("udp", ":56780") if err != nil { - log.Printf("Failed to resolve UDP address %s: %v", broadcastAddr, err) - return err + log.Fatalf("error setting up listener: %v", err) } + defer listener.Close() - // 使用本地地址进行连接 - localAddr, err := net.ResolveUDPAddr("udp4", "0.0.0.0:0") - if err != nil { - log.Printf("Failed to resolve local UDP address: %v", err) - return err - } + log.Println("UDP server started on :56780") - conn, err := net.ListenUDP("udp4", localAddr) - if err != nil { - log.Printf("Failed to listen on UDP address %s: %v", broadcastAddr, err) - return err - } - defer conn.Close() + // 无限循环,监听 UDP 请求 + for { + buffer := make([]byte, 1024) - data, err := json.Marshal(message) - if err != nil { - log.Printf("Failed to marshal JSON for %s: %v", broadcastAddr, err) - return err - } + n, remoteAddr, err := listener.ReadFrom(buffer) + if err != nil { + log.Printf("error reading from UDP: %v", err) + continue + } - _, err = conn.WriteToUDP(data, addr) - if err != nil { - log.Printf("Failed to write to UDP address %s: %v", broadcastAddr, err) - return err + log.Printf("Received UDP packet from %v: %s", remoteAddr, buffer[:n]) + // 解析 UDP 数据 + var udpMsg UdpMessage + err = json.Unmarshal(buffer[:n], &udpMsg) + if err != nil { + log.Printf("error unmarshalling UDP message: %v", err) + continue + } + if udpMsg.Type == "heartbeat" { + UpdateUserStatus(udpMsg.IP, udpMsg.Hostname) + continue + } + if udpMsg.Type == "file" { + RecieveFile(udpMsg) + continue + } + // 添加消息到 UserMessages + AddMessage(udpMsg) } - - log.Printf("发送消息到 %s 成功", broadcastAddr) - return nil +} +func ClearAllUserMessages() { + for ip, msg := range UserMessages { + msg.Messages = []UdpMessage{} // 清空切片 + UserMessages[ip] = msg // 更新映射中的值 + } +} +func GetMessages() UserMessage { + return UserMessage{ + Messages: UserMessages, + Onlines: OnlineUsers, + } +} +func AddMessage(msg UdpMessage) { + if _, ok := UserMessages[msg.IP]; !ok { + UserMessages[msg.IP] = &Messages{} + } + UserMessages[msg.IP].Messages = append(UserMessages[msg.IP].Messages, msg) } diff --git a/godo/localchat/udp.go b/godo/localchat/udp.go deleted file mode 100644 index 9d2a65a..0000000 --- a/godo/localchat/udp.go +++ /dev/null @@ -1,141 +0,0 @@ -package localchat - -import ( - "encoding/json" - "godo/libs" - "log" - "net" - "strings" - "time" -) - -type UdpMessage struct { - Hostname string `json:"hostname"` - Type string `json:"type"` - Time time.Time `json:"time"` - IP string `json:"ip"` - Message any `json:"message"` -} -type UdpAddress struct { - Hostname string `json:"hostname"` - IP string `json:"ip"` - Time time.Time `json:"time"` -} -type Messages struct { - Messages []UdpMessage `json:"messages"` -} -type UserMessage struct { - Messages map[string]*Messages `json:"messages"` - Onlines []UdpAddress `json:"onlines"` -} - -var OnlineUsers []UdpAddress -var UserMessages = make(map[string]*Messages) // 使用指针类型 - -func init() { - go InitBroadcast() - go ListenForBroadcast() -} - -// ListenForBroadcast 监听多播消息 -func ListenForBroadcast() { - broadcastAddr := GetBroadcastAddr() - addr, err := net.ResolveUDPAddr("udp4", broadcastAddr) - if err != nil { - log.Fatalf("Failed to resolve UDP address: %v", err) - } - // 使用 ListenMulticastUDP 创建多播连接 - conn, err := net.ListenMulticastUDP("udp4", nil, addr) - if err != nil { - log.Fatalf("Failed to listen on UDP address: %v", err) - } - defer conn.Close() - - // 获取本地 IP 地址 - ips, err := libs.GetValidIPAddresses() - if err != nil { - log.Fatalf("Failed to get local IP addresses: %v", err) - } - - // 开始监听多播消息 - buffer := make([]byte, 1024) - for { - n, remoteAddr, err := conn.ReadFromUDP(buffer) - if err != nil { - log.Printf("Error reading from UDP: %v", err) - continue - } - - var udpMsg UdpMessage - err = json.Unmarshal(buffer[:n], &udpMsg) - if err != nil { - log.Printf("Error unmarshalling JSON: %v", err) - continue - } - // 从 remoteAddr 获取 IP 地址 - ip := remoteAddr.IP.String() - if containArr(ips, ip) { - continue - } - if udpMsg.Type == "online" { - if !containIp(OnlineUsers, ip) { - OnlineUsers = append(OnlineUsers, UdpAddress{Hostname: udpMsg.Hostname, IP: ip, Time: time.Now()}) - log.Printf("在线用户: %v", OnlineUsers) - } - } - udpMsg.IP = ip - if udpMsg.Type == "text" { - addMessageToUserMessages(ip, udpMsg) - } - if udpMsg.Type == "file" { - addMessageToUserMessages(ip, udpMsg) - RecieveFile(udpMsg) - } - log.Printf("Received message from %s: %s", remoteAddr, udpMsg.Hostname) - } -} -func containArr(s []string, str string) bool { - for _, v := range s { - if v == str { - return true - } - } - return false -} -func containIp(slice []UdpAddress, element string) bool { - for _, v := range slice { - if v.IP == element { - return true - } - } - return false -} - -// 添加消息到 UserMessages -func addMessageToUserMessages(ip string, msg UdpMessage) { - if _, ok := UserMessages[ip]; !ok { - UserMessages[ip] = &Messages{} - } - UserMessages[ip].Messages = append(UserMessages[ip].Messages, msg) -} - -// 清空 UserMessages 中所有 IP 的消息列表 -func ClearAllUserMessages() { - for ip, msg := range UserMessages { - msg.Messages = []UdpMessage{} // 清空切片 - UserMessages[ip] = msg // 更新映射中的值 - } -} -func GetMessages() UserMessage { - return UserMessage{ - Messages: UserMessages, - Onlines: OnlineUsers, - } -} -func GetBroadcastAddr() string { - return libs.GetUdpAddr() -} -func GetBroadcastPort() string { - addr := GetBroadcastAddr() - return addr[strings.LastIndex(addr, ":")+1:] -}