mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-06 00:16:49 +08:00
feat: implement websocket and UI
This commit is contained in:
210
sio/hub.go
Normal file
210
sio/hub.go
Normal file
@@ -0,0 +1,210 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user