GodoOS 是一款轻量级的云桌面系统,旨在为用户提供高效、安全的网络通信环境。其内网聊天功能不仅支持文本消息的实时传输,还具备文件传输的能力。本文将详细介绍 godoos 内网聊天机制的核心原理和技术实现。
内网聊天机制概述
godoos 的内网聊天机制基于 UDP 协议,通过定期检查在线用户并维护一个在线用户列表,确保消息只能发送给当前在线的用户。此外,文件传输功能需要用户明确同意接收,以保证数据的安全性和用户的隐私。
ARP 发现在线 IP:
- godoos 使用 ARP 协议定期扫描局域网内的所有设备,发现在线的 IP 地址。
- 这一过程通过
CheckOnlines
函数实现,该函数每 15 秒(可配置)调用一次CheckOnline
方法,检查客户端是否已断开连接。
func getArpCacheIPs() ([]string, error) {var cmd *exec.Cmdvar out []bytevar err errorswitch runtime.GOOS {case "windows":cmd = exec.Command("arp", "-a")case "linux":cmd = exec.Command("arp", "-n")case "darwin": // macOScmd = exec.Command("arp", "-l", "-a")default:return nil, fmt.Errorf("unsupported operating system: %v", runtime.GOOS)}out, err = cmd.Output()if err != nil {return nil, fmt.Errorf("error executing arp command: %v", err)}lines := strings.Split(string(out), "\n")var ips []stringfor _, line := range lines {fields := strings.Fields(line)if len(fields) >= 2 {ip := fields[0]if ip != "<incomplete>" && net.ParseIP(ip) != nil {ips = append(ips, ip)}}}return ips, nil
}
维护在线用户列表:
- 所有在线的用户信息被存储在
OnlineUsers
字典中,包括主机名、IP 地址、最后活跃时间等。 - 当用户发送心跳消息(类型为
heartbeat
)时,系统会更新该用户的在线状态。
func concurrentGetIpInfo(ips []string) {// 获取本地 IP 地址hostips, err := libs.GetValidIPAddresses()if err != nil {log.Printf("failed to get local IP addresses: %v", err)return}// 获取 ARP 缓存中的 IP 地址validIPs, err := getArpCacheIPs()if err != nil {log.Printf("failed to get ARP cache IPs: %v", err)return}var wg sync.WaitGroupmaxConcurrency := runtime.NumCPU()semaphore := make(chan struct{}, maxConcurrency)failedIPs := make(map[string]bool)for _, ip := range ips {if containArr(hostips, ip) || failedIPs[ip] || !containArr(validIPs, 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}}(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)
}
消息传输
UDP 服务器:
- godoos 在 UDP 端口 56780 上启动一个监听服务,接收来自客户端的消息。
- 接收到的消息首先进行 JSON 解析,提取出消息类型、发送方 IP 地址等信息。
func UdpServer() {// 监听 UDP 端口listener, err := net.ListenPacket("udp", ":56780")if err != nil {log.Fatalf("error setting up listener: %v", err)}defer listener.Close()log.Println("UDP server started on :56780")// 监听 UDP 请求for {buffer := make([]byte, 1024)n, remoteAddr, err := listener.ReadFrom(buffer)if err != nil {log.Printf("error reading from UDP: %v", err)continue}log.Printf("Received UDP packet from %v: %s", remoteAddr, buffer[:n])// 从 remoteAddr 获取 IP 地址udpAddr, ok := remoteAddr.(*net.UDPAddr)if !ok {log.Printf("unexpected address type: %T", remoteAddr)continue}ip := udpAddr.IP.String()// 解析 UDP 数据var udpMsg UdpMessageerr = json.Unmarshal(buffer[:n], &udpMsg)if err != nil {log.Printf("error unmarshalling UDP message: %v", err)continue}udpMsg.IP = ipif udpMsg.Type == "heartbeat" {UpdateUserStatus(udpMsg.IP, udpMsg.Hostname)continue}if udpMsg.Type == "image" {filePath, err := ReceiveImg(udpMsg)if err != nil {log.Printf("error receiving image: %v", err)continue}udpMsg.Message = filePath}// 添加消息到 UserMessagesAddMessage(udpMsg)}
}
消息处理:
- 根据消息类型,系统会调用相应的处理函数。例如,心跳消息用于更新用户状态,文件传输请求需要用户确认。
- 所有消息会被存储在
UserMessages
字典中,以便后续查询和展示。
文件传输
-
文件发送请求:
- 用户发起文件发送请求时,系统会生成一条类型为
fileSending
的消息,并发送给目标用户。 - 目标用户接收到请求后,可以选择接受或拒绝。
- 用户发起文件发送请求时,系统会生成一条类型为
-
文件传输确认:
- 如果目标用户同意接收文件,系统会生成一条类型为
fileAccessed
的消息,通知发送方开始传输文件。 - 文件传输完成后,系统会返回文件路径给发送方,确保文件传输成功。
- 如果目标用户同意接收文件,系统会生成一条类型为
-
文件传输取消:
- 用户也可以取消文件发送请求,系统会生成一条类型为
fileCannel
的消息,通知目标用户取消操作。
- 用户也可以取消文件发送请求,系统会生成一条类型为
func HandlerApplySendFile(w http.ResponseWriter, r *http.Request) {var msg UdpMessagedecoder := json.NewDecoder(r.Body)if err := decoder.Decode(&msg); err != nil {http.Error(w, "Invalid request body", http.StatusBadRequest)return}defer r.Body.Close()hostname, err := os.Hostname()if err != nil {libs.ErrorMsg(w, "HandleMessage error")return}msg.Hostname = hostnamemsg.Time = time.Now()msg.Type = "fileSending"SendToIP(msg)libs.SuccessMsg(w, nil, "请求文件发送成功")
}
func HandlerCannelFile(w http.ResponseWriter, r *http.Request) {var msg UdpMessagedecoder := json.NewDecoder(r.Body)if err := decoder.Decode(&msg); err != nil {http.Error(w, "Invalid request body", http.StatusBadRequest)return}defer r.Body.Close()hostname, err := os.Hostname()if err != nil {libs.ErrorMsg(w, "HandleMessage error")return}msg.Hostname = hostnamemsg.Time = time.Now()msg.Type = "fileCannel"SendToIP(msg)libs.SuccessMsg(w, nil, "请求文件发送成功")
}
func HandlerAccessFile(w http.ResponseWriter, r *http.Request) {var msg UdpMessagedecoder := json.NewDecoder(r.Body)if err := decoder.Decode(&msg); err != nil {libs.ErrorMsg(w, "Invalid request body")return}defer r.Body.Close()hostname, err := os.Hostname()if err != nil {libs.ErrorMsg(w, "HandleMessage error")return}msg.Hostname = hostnamemsg.Time = time.Now()msg.Type = "fileAccessed"SendToIP(msg)path, err := downloadFiles(msg)if err != nil {libs.ErrorMsg(w, "HandleMessage error")return}res := map[string]interface{}{"path": path,"msg": msg.Message,}libs.SuccessMsg(w, res, "接收文件成功")
}
安全与隐私
- 用户确认机制:文件传输需要目标用户明确同意,确保数据的安全性和用户的隐私。
- 在线状态检查:系统定期检查在线用户,确保消息只能发送给当前在线的用户,避免无效消息的传输。
结语
godoos 的内网聊天机制通过高效的在线用户检测和严格的消息处理流程,确保了内网通信的实时性和安全性。无论是文本消息还是文件传输,用户都能享受到便捷、可靠的通信体验。未来,godoos 将继续优化这一机制,为用户提供更加完善的内网通信解决方案。