package websocket import ( "encoding/json" "log" "sync" "time" ) // RoomConfig 房间配置参数 type RoomConfig struct { MaxConnections int // 最大连接数 MessageBuffer int // 消息缓冲区大小 PingInterval time.Duration // ping消息发送间隔 } // DefaultRoomConfig 返回默认的房间配置 func DefaultRoomConfig() *RoomConfig { return &RoomConfig{ MaxConnections: 100, MessageBuffer: 256, PingInterval: 30 * time.Second, } } // BroadcastPayload 广播消息的负载结构 type BroadcastPayload struct { Message *WSMessage // 要广播的消息 } // DirectPayload 私信消息的负载结构 type DirectPayload struct { To string // 接收者ID Message *WSMessage // 要发送的消息 } // 全局房间管理 var rooms = make(map[string]*Room) var roomsMutex sync.Mutex // DeleteRoom 从全局房间管理器中删除指定房间 func DeleteRoom(roomID string) { roomsMutex.Lock() defer roomsMutex.Unlock() delete(rooms, roomID) log.Printf("房间[%s]已从全局房间管理器删除", roomID) } // Room 表示一个聊天房间 type Room struct { ID string // 房间ID Connections sync.Map // 房间内的连接 OnlineUsers sync.Map // 在线用户 Register chan *Connection // 注册通道 Unregister chan *Connection // 注销通道 Broadcast chan *BroadcastPayload // 广播通道 Direct chan *DirectPayload // 私信通道 Quit chan struct{} // 退出通道 config *RoomConfig // 房间配置 stats *RoomStats // 房间统计信息 closed bool // 房间是否已关闭 mu sync.RWMutex // 读写锁 } // RoomStats 房间统计信息 type RoomStats struct { TotalMessages int64 // 总消息数 TotalConnections int64 // 总连接数 StartTime time.Time // 开始时间 mu sync.RWMutex // 读写锁 } // NewRoomStats 创建新的房间统计对象 func NewRoomStats() *RoomStats { return &RoomStats{ StartTime: time.Now(), } } // IncrementMessages 增加消息计数 func (rs *RoomStats) IncrementMessages() { rs.mu.Lock() defer rs.mu.Unlock() rs.TotalMessages++ } // IncrementConnections 增加连接计数 func (rs *RoomStats) IncrementConnections() { rs.mu.Lock() defer rs.mu.Unlock() rs.TotalConnections++ } // GetStats 获取统计信息 func (rs *RoomStats) GetStats() map[string]interface{} { rs.mu.RLock() defer rs.mu.RUnlock() return map[string]interface{}{ "total_messages": rs.TotalMessages, "total_connections": rs.TotalConnections, "uptime": time.Since(rs.StartTime).String(), } } // NewRoom 创建新的房间 func NewRoom(id string, maxConnections int) *Room { config := DefaultRoomConfig() if maxConnections > 0 { config.MaxConnections = maxConnections } r := &Room{ ID: id, Register: make(chan *Connection), Unregister: make(chan *Connection), Broadcast: make(chan *BroadcastPayload), Direct: make(chan *DirectPayload), Quit: make(chan struct{}), config: config, stats: NewRoomStats(), } go r.Run() go func() { <-r.Quit log.Printf("房间[%s]销毁成功", r.ID) DeleteRoom(r.ID) // 从全局管理器删除 }() return r } // Run 运行房间的主循环 func (r *Room) Run() { ticker := time.NewTicker(r.config.PingInterval) defer func() { ticker.Stop() r.cleanup() }() for { select { case conn := <-r.Register: r.mu.RLock() if r.closed { r.mu.RUnlock() return } r.mu.RUnlock() log.Printf("客户端 %s 注册到房间 %s", conn.UID, r.ID) if r.GetConnectionCount() >= r.config.MaxConnections { log.Printf("房间 %s: 已达到最大连接数", r.ID) conn.Send <- &WSMessage{ Type: "error", Content: "房间已满", } conn.Conn.Close() continue } r.Connections.Store(conn, true) r.OnlineUsers.Store(conn.UID, true) r.stats.IncrementConnections() r.broadcastRoomInfo() case conn := <-r.Unregister: r.mu.RLock() if r.closed { r.mu.RUnlock() return } r.mu.RUnlock() log.Printf("客户端 %s 从房间 %s 注销", conn.UID, r.ID) r.Connections.Delete(conn) r.OnlineUsers.Delete(conn.UID) r.broadcastRoomInfo() case payload := <-r.Broadcast: r.mu.RLock() if r.closed { r.mu.RUnlock() return } r.mu.RUnlock() log.Printf("在房间 %s 中广播消息: %s", r.ID, payload.Message.Content) r.stats.IncrementMessages() r.broadcastMessage(payload.Message) case payload := <-r.Direct: r.mu.RLock() if r.closed { r.mu.RUnlock() return } r.mu.RUnlock() log.Printf("发送私信给 %s: %s", payload.To, payload.Message.Content) r.stats.IncrementMessages() r.sendDirectMessage(payload) case <-ticker.C: r.mu.RLock() if r.closed { r.mu.RUnlock() return } r.mu.RUnlock() r.broadcastRoomInfo() case <-r.Quit: return } } } // broadcastMessage 广播消息给房间内所有用户 func (r *Room) broadcastMessage(msg *WSMessage) { r.Connections.Range(func(key, value interface{}) bool { conn := key.(*Connection) log.Printf("广播消息给客户端 %s", conn.UID) select { case conn.Send <- msg: log.Printf("消息已发送给客户端 %s", conn.UID) default: log.Printf("发送消息给客户端 %s 失败", conn.UID) r.Connections.Delete(conn) close(conn.Send) } return true }) } // sendDirectMessage 发送私信给指定用户 func (r *Room) sendDirectMessage(payload *DirectPayload) { // 获取hub实例 hub := GetHub() if hub == nil { log.Printf("无法获取hub实例") return } // 使用hub发送跨房间私信 hub.SendDirectMessage(payload.Message.From, payload.To, payload.Message) } // broadcastRoomInfo 广播房间信息 func (r *Room) broadcastRoomInfo() { info := map[string]interface{}{ "type": "room_info", "room_id": r.ID, "connections": r.GetConnectionCount(), "online_users": r.GetOnlineUserCount(), "stats": r.stats.GetStats(), } infoJSON, _ := json.Marshal(info) msg := &WSMessage{ Type: "room_info", Content: string(infoJSON), Time: time.Now(), } r.broadcastMessage(msg) } // GetConnectionCount 获取房间内的连接数 func (r *Room) GetConnectionCount() int { count := 0 r.Connections.Range(func(key, value interface{}) bool { count++ return true }) return count } // GetOnlineUserCount 获取房间内的在线用户数 func (r *Room) GetOnlineUserCount() int { count := 0 r.OnlineUsers.Range(func(key, value interface{}) bool { count++ return true }) return count } // cleanup 清理房间资源 func (r *Room) cleanup() { r.mu.Lock() defer r.mu.Unlock() if r.closed { return } r.closed = true r.Connections.Range(func(key, value interface{}) bool { conn := key.(*Connection) r.Connections.Delete(conn) return true }) close(r.Register) close(r.Unregister) close(r.Broadcast) close(r.Direct) close(r.Quit) } // Shutdown 关闭房间 func (r *Room) Shutdown() { r.mu.Lock() if !r.closed { close(r.Quit) } r.mu.Unlock() }