godoos 内网聊天机制详解
GodoOS 是一款轻量级的云桌面系统,旨在为用户提供高效、安全的网络通信环境。其内网聊天功能不仅支持文本消息的实时传输,还具备文件传输的能力。本文将详细介绍 godoos 内网聊天机制的核心原理和技术实现。
内网聊天机制概述
godoos 的内网聊天机制基于 UDP 协议,通过定期检查在线用户并维护一个在线用户列表,确保消息只能发送给当前在线的用户。此外,文件传输功能需要用户明确同意接收,以保证数据的安全性和用户的隐私。
ARP 发现在线 IP:
- godoos 使用 ARP 协议定期扫描局域网内的所有设备,发现在线的 IP 地址。
- 这一过程通过
CheckOnlines
函数实现,该函数每 15 秒(可配置)调用一次CheckOnline
方法,检查客户端是否已断开连接。
func getArpCacheIPs() ([]string, error) {
var cmd *exec.Cmd
var out []byte
var err error
switch runtime.GOOS {
case "windows":
cmd = exec.Command("arp", "-a")
case "linux":
cmd = exec.Command("arp", "-n")
case "darwin": // macOS
cmd = 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 []string
for _, 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.WaitGroup
maxConcurrency := 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 UdpMessage
err = json.Unmarshal(buffer[:n], &udpMsg)
if err != nil {
log.Printf("error unmarshalling UDP message: %v", err)
continue
}
udpMsg.IP = ip
if 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
}
// 添加消息到 UserMessages
AddMessage(udpMsg)
}
}
消息处理:
- 根据消息类型,系统会调用相应的处理函数。例如,心跳消息用于更新用户状态,文件传输请求需要用户确认。
- 所有消息会被存储在
UserMessages
字典中,以便后续查询和展示。
文件传输
-
文件发送请求:
- 用户发起文件发送请求时,系统会生成一条类型为
fileSending
的消息,并发送给目标用户。 - 目标用户接收到请求后,可以选择接受或拒绝。
- 用户发起文件发送请求时,系统会生成一条类型为
-
文件传输确认:
- 如果目标用户同意接收文件,系统会生成一条类型为
fileAccessed
的消息,通知发送方开始传输文件。 - 文件传输完成后,系统会返回文件路径给发送方,确保文件传输成功。
- 如果目标用户同意接收文件,系统会生成一条类型为
-
文件传输取消:
- 用户也可以取消文件发送请求,系统会生成一条类型为
fileCannel
的消息,通知目标用户取消操作。
- 用户也可以取消文件发送请求,系统会生成一条类型为
func HandlerApplySendFile(w http.ResponseWriter, r *http.Request) {
var msg UdpMessage
decoder := 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 = hostname
msg.Time = time.Now()
msg.Type = "fileSending"
SendToIP(msg)
libs.SuccessMsg(w, nil, "请求文件发送成功")
}
func HandlerCannelFile(w http.ResponseWriter, r *http.Request) {
var msg UdpMessage
decoder := 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 = hostname
msg.Time = time.Now()
msg.Type = "fileCannel"
SendToIP(msg)
libs.SuccessMsg(w, nil, "请求文件发送成功")
}
func HandlerAccessFile(w http.ResponseWriter, r *http.Request) {
var msg UdpMessage
decoder := 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 = hostname
msg.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 将继续优化这一机制,为用户提供更加完善的内网通信解决方案。