mirror of
https://github.com/oarkflow/mq.git
synced 2025-09-26 20:11:16 +08:00
211 lines
4.7 KiB
Go
211 lines
4.7 KiB
Go
package sio
|
|
|
|
import (
|
|
"slices"
|
|
"sync"
|
|
)
|
|
|
|
type hub struct {
|
|
sockets map[string]*Socket
|
|
rooms map[string]*room
|
|
shutdownCh chan bool
|
|
socketList chan []*Socket
|
|
addCh chan *Socket
|
|
delCh chan *Socket
|
|
joinRoomCh chan *joinRequest
|
|
leaveRoomCh chan *leaveRequest
|
|
roomMsgCh chan *RoomMsg
|
|
broomcastCh chan *RoomMsg // for passing data from the backend
|
|
broadcastCh chan *BroadcastMsg
|
|
bbroadcastCh chan *BroadcastMsg
|
|
multihomeEnabled bool
|
|
multihomeBackend Adapter
|
|
l sync.RWMutex
|
|
}
|
|
|
|
type room struct {
|
|
name string
|
|
sockets map[string]*Socket
|
|
l sync.RWMutex
|
|
}
|
|
|
|
type joinRequest struct {
|
|
roomName string
|
|
socket *Socket
|
|
}
|
|
|
|
type leaveRequest struct {
|
|
roomName string
|
|
socket *Socket
|
|
}
|
|
|
|
// RoomMsg represents an event to be dispatched to a room of sockets
|
|
type RoomMsg struct {
|
|
RoomName string
|
|
Except []string
|
|
EventName string
|
|
Data any
|
|
}
|
|
|
|
// BroadcastMsg represents an event to be dispatched to all Sockets on the Server
|
|
type BroadcastMsg struct {
|
|
EventName string
|
|
Except []string
|
|
Data any
|
|
}
|
|
|
|
func (h *hub) addSocket(s *Socket) {
|
|
h.addCh <- s
|
|
}
|
|
|
|
func (h *hub) removeSocket(s *Socket) {
|
|
h.delCh <- s
|
|
}
|
|
|
|
func (h *hub) joinRoom(j *joinRequest) {
|
|
h.joinRoomCh <- j
|
|
}
|
|
|
|
func (h *hub) leaveRoom(l *leaveRequest) {
|
|
h.leaveRoomCh <- l
|
|
}
|
|
|
|
func (h *hub) toRoom(msg *RoomMsg) {
|
|
h.roomMsgCh <- msg
|
|
}
|
|
|
|
func (h *hub) broadcast(b *BroadcastMsg) {
|
|
h.broadcastCh <- b
|
|
}
|
|
|
|
func (h *hub) setMultihomeBackend(b Adapter) {
|
|
if h.multihomeEnabled {
|
|
return // can't have two backends... yet
|
|
}
|
|
|
|
h.multihomeBackend = b
|
|
h.multihomeEnabled = true
|
|
|
|
h.multihomeBackend.Init()
|
|
|
|
go h.multihomeBackend.BroadcastFromBackend(h.bbroadcastCh)
|
|
go h.multihomeBackend.RoomcastFromBackend(h.broomcastCh)
|
|
}
|
|
|
|
func (h *hub) listen() {
|
|
for {
|
|
select {
|
|
case c := <-h.addCh:
|
|
h.l.Lock()
|
|
h.sockets[c.ID()] = c
|
|
h.l.Unlock()
|
|
case c := <-h.delCh:
|
|
delete(h.sockets, c.ID())
|
|
case c := <-h.joinRoomCh:
|
|
if _, exists := h.rooms[c.roomName]; !exists { // make the room if it doesn't exist
|
|
h.rooms[c.roomName] = &room{name: c.roomName, sockets: make(map[string]*Socket)}
|
|
}
|
|
h.rooms[c.roomName].l.Lock()
|
|
h.rooms[c.roomName].sockets[c.socket.ID()] = c.socket
|
|
h.rooms[c.roomName].l.Unlock()
|
|
case c := <-h.leaveRoomCh:
|
|
if room, exists := h.rooms[c.roomName]; exists {
|
|
room.l.Lock()
|
|
delete(room.sockets, c.socket.ID())
|
|
room.l.Unlock()
|
|
if len(room.sockets) == 0 { // room is empty, delete it
|
|
delete(h.rooms, c.roomName)
|
|
}
|
|
}
|
|
case c := <-h.roomMsgCh:
|
|
if room, exists := h.rooms[c.RoomName]; exists {
|
|
room.l.Lock()
|
|
for _, s := range room.sockets {
|
|
if len(c.Except) > 0 {
|
|
if !slices.Contains(c.Except, s.ID()) {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
} else {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
}
|
|
room.l.Unlock()
|
|
}
|
|
if h.multihomeEnabled { // the room may exist on the other end
|
|
go h.multihomeBackend.RoomcastToBackend(c)
|
|
}
|
|
case c := <-h.broomcastCh:
|
|
if room, exists := h.rooms[c.RoomName]; exists {
|
|
room.l.Lock()
|
|
for _, s := range room.sockets {
|
|
if len(c.Except) > 0 {
|
|
if !slices.Contains(c.Except, s.ID()) {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
} else {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
}
|
|
room.l.Unlock()
|
|
}
|
|
case c := <-h.broadcastCh:
|
|
h.l.Lock()
|
|
for _, s := range h.sockets {
|
|
if len(c.Except) > 0 {
|
|
if !slices.Contains(c.Except, s.ID()) {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
} else {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
}
|
|
h.l.Unlock()
|
|
if h.multihomeEnabled {
|
|
go h.multihomeBackend.BroadcastToBackend(c)
|
|
}
|
|
case c := <-h.bbroadcastCh:
|
|
h.l.Lock()
|
|
for _, s := range h.sockets {
|
|
if len(c.Except) > 0 {
|
|
if !slices.Contains(c.Except, s.ID()) {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
} else {
|
|
s.Emit(c.EventName, c.Data)
|
|
}
|
|
}
|
|
h.l.Unlock()
|
|
case _ = <-h.shutdownCh:
|
|
var socketList []*Socket
|
|
h.l.Lock()
|
|
for _, s := range h.sockets {
|
|
socketList = append(socketList, s)
|
|
}
|
|
h.l.Unlock()
|
|
h.socketList <- socketList
|
|
}
|
|
}
|
|
}
|
|
|
|
func newHub() *hub {
|
|
h := &hub{
|
|
shutdownCh: make(chan bool),
|
|
socketList: make(chan []*Socket),
|
|
sockets: make(map[string]*Socket),
|
|
rooms: make(map[string]*room),
|
|
addCh: make(chan *Socket),
|
|
delCh: make(chan *Socket),
|
|
joinRoomCh: make(chan *joinRequest),
|
|
leaveRoomCh: make(chan *leaveRequest),
|
|
roomMsgCh: make(chan *RoomMsg),
|
|
broomcastCh: make(chan *RoomMsg),
|
|
broadcastCh: make(chan *BroadcastMsg),
|
|
bbroadcastCh: make(chan *BroadcastMsg),
|
|
multihomeEnabled: false,
|
|
}
|
|
|
|
go h.listen()
|
|
|
|
return h
|
|
}
|