This repository has been archived by the owner on Sep 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 840
/
broadcast.go
167 lines (132 loc) · 4.56 KB
/
broadcast.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package socketio
import "sync"
// EachFunc typed for each callback function
type EachFunc func(Conn)
// Broadcast is the adaptor to handle broadcasts & rooms for socket.io server API
type Broadcast interface {
Join(room string, connection Conn) // Join causes the connection to join a room
Leave(room string, connection Conn) // Leave causes the connection to leave a room
LeaveAll(connection Conn) // LeaveAll causes given connection to leave all rooms
Clear(room string) // Clear causes removal of all connections from the room
Send(room, event string, args ...interface{}) // Send will send an event with args to the room
SendAll(event string, args ...interface{}) // SendAll will send an event with args to all the rooms
ForEach(room string, f EachFunc) // ForEach sends data by DataFunc, if room does not exits sends nothing
Len(room string) int // Len gives number of connections in the room
Rooms(connection Conn) []string // Gives list of all the rooms if no connection given, else list of all the rooms the connection joined
AllRooms() []string // Gives list of all the rooms the connection joined
}
// broadcast gives Join, Leave & BroadcastTO server API support to socket.io along with room management
// map of rooms where each room contains a map of connection id to connections in that room
type broadcast struct {
rooms map[string]map[string]Conn
lock sync.RWMutex
}
// newBroadcast creates a new broadcast adapter
func newBroadcast() *broadcast {
return &broadcast{
rooms: make(map[string]map[string]Conn),
}
}
// Join joins the given connection to the broadcast room
func (bc *broadcast) Join(room string, connection Conn) {
bc.lock.Lock()
defer bc.lock.Unlock()
if _, ok := bc.rooms[room]; !ok {
bc.rooms[room] = make(map[string]Conn)
}
bc.rooms[room][connection.ID()] = connection
}
// Leave leaves the given connection from given room (if exist)
func (bc *broadcast) Leave(room string, connection Conn) {
bc.lock.Lock()
defer bc.lock.Unlock()
if connections, ok := bc.rooms[room]; ok {
delete(connections, connection.ID())
if len(connections) == 0 {
delete(bc.rooms, room)
}
}
}
// LeaveAll leaves the given connection from all rooms
func (bc *broadcast) LeaveAll(connection Conn) {
bc.lock.Lock()
defer bc.lock.Unlock()
for room, connections := range bc.rooms {
delete(connections, connection.ID())
if len(connections) == 0 {
delete(bc.rooms, room)
}
}
}
// Clear clears the room
func (bc *broadcast) Clear(room string) {
bc.lock.Lock()
defer bc.lock.Unlock()
delete(bc.rooms, room)
}
// Send sends given event & args to all the connections in the specified room
func (bc *broadcast) Send(room, event string, args ...interface{}) {
bc.lock.RLock()
defer bc.lock.RUnlock()
for _, connection := range bc.rooms[room] {
connection.Emit(event, args...)
}
}
// SendAll sends given event & args to all the connections to all the rooms
func (bc *broadcast) SendAll(event string, args ...interface{}) {
bc.lock.RLock()
defer bc.lock.RUnlock()
for _, connections := range bc.rooms {
for _, connection := range connections {
connection.Emit(event, args...)
}
}
}
// ForEach sends data returned by DataFunc, if room does not exits sends nothing
func (bc *broadcast) ForEach(room string, f EachFunc) {
bc.lock.RLock()
defer bc.lock.RUnlock()
occupants, ok := bc.rooms[room]
if !ok {
return
}
for _, connection := range occupants {
f(connection)
}
}
// Len gives number of connections in the room
func (bc *broadcast) Len(room string) int {
bc.lock.RLock()
defer bc.lock.RUnlock()
return len(bc.rooms[room])
}
// Rooms gives the list of all the rooms available for broadcast in case of
// no connection is given, in case of a connection is given, it gives
// list of all the rooms the connection is joined to
func (bc *broadcast) Rooms(connection Conn) []string {
if connection == nil {
return bc.AllRooms()
}
bc.lock.RLock()
defer bc.lock.RUnlock()
return bc.getRoomsByConn(connection)
}
// AllRooms gives list of all rooms available for broadcast
func (bc *broadcast) AllRooms() []string {
bc.lock.RLock()
defer bc.lock.RUnlock()
rooms := make([]string, 0, len(bc.rooms))
for room := range bc.rooms {
rooms = append(rooms, room)
}
return rooms
}
func (bc *broadcast) getRoomsByConn(connection Conn) []string {
var rooms []string
for room, connections := range bc.rooms {
if _, ok := connections[connection.ID()]; ok {
rooms = append(rooms, room)
}
}
return rooms
}