ΠΡΠΈΠ²Π΅Ρ, ΠΌΠ½Π΅ Π±ΡΠ»ΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ, ΠΊΠ°ΠΊ Ρ Π±ΡΠ΄Ρ Π΄Π΅Π»Π°ΡΡ Β«ΠΊΠΎΠΌΠ½Π°ΡΡΒ» ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠΉ Ρ ΠΏΠΎΠΌΠΎΡΡΡ gorilla websocket.
Π― ΡΡΡΠ°Π½ΠΎΠ²ΠΈΠ» ΠΏΡΠΎΡΡΠΎΠΉ ΡΠ°Ρ-ΡΠ΅ΡΠ²Π΅Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΡΠ°Π½ΡΠ»ΠΈΡΡΠ΅Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ Π²ΡΠ΅ΠΌ, Π½ΠΎ ΠΌΠ½Π΅ Π±ΡΠ»ΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ, ΠΊΠ°ΠΊ Π±Ρ Ρ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π» ΠΊΠΎΠΌΠ½Π°ΡΡ.
ΠΠΎΡ Π½Π΅ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΠΎΠ²Π°Π½Π½Π°Ρ ΠΈ Π½Π΅ΠΏΡΠΎΠ²Π΅ΡΠ΅Π½Π½Π°Ρ ΠΌΠΎΠ΄ΠΈΡΠΈΠΊΠ°ΡΠΈΡ Ρ Π°Π±Π° ΠΈΠ· ΠΏΡΠΈΠΌΠ΅ΡΠ° Gorilla. Go to support rooms:
type message struct {
data []byte
room string
}
type subscription struct {
conn *connection
room string
}
// hub maintains the set of active connections and broadcasts messages to the
// connections.
type hub struct {
// Registered connections.
rooms map[strng]map[*connection]bool
// Inbound messages from the connections.
broadcast chan message
// Register requests from the connections.
register chan subscription
// Unregister requests from connections.
unregister chan subscription
}
func (h *hub) run() {
for {
select {
case s := <-h.register:
connections := h.rooms[sub.rooom]
if connections == nil {
connections = make(map[*connection]bool)
h.rooms[s.room] = connections
}
connections[s.conn] = true
case s := <-h.unregister:
connections := h.rooms[s.rooom]
if connections != nil {
if _, ok := connections[s.conn]; ok {
delete(connections, s.conn)
close(s.conn.send)
if len(connections) == 0 {
delete(h.rooms, s.room)
}
}
}
case m := <-h.broadcast:
connections := h.rooms[m.rooom]
for c := range h.connections {
select {
case c.send <- m.data:
default:
close(c.send)
delete(h.connections, c)
if len(connections) == 0 {
delete(h.rooms, m.room)
}
}
}
}
}
}
ΠΠ·ΠΌΠ΅Π½ΠΈΡΠ΅ ΡΠΈΠΏ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΈΠΌΡ ΠΊΠΎΠΌΠ½Π°ΡΡ ΠΈΠ· ΡΡΡΠΎΠΊΠΈ Π·Π°ΠΏΡΠΎΡΠ° ΠΈΠ»ΠΈ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ, ΠΈ Π²ΠΊΠ»ΡΡΠΈΡΠ΅ ΡΡΠΎ ΠΈΠΌΡ ΠΊΠΎΠΌΠ½Π°ΡΡ Π²ΠΎ Π²ΡΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΡ, ΠΎΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌΡΠ΅ Π½Π° ΠΊΠ°Π½Π°Π»Ρ ΠΊΠΎΠ½ΡΠ΅Π½ΡΡΠ°ΡΠΎΡΠ°.
Π£ ΠΌΠ΅Π½Ρ Π½Π΅Ρ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΏΠΈΡΠ°ΡΡ ΠΈ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°ΡΡ ΠΏΠΎΠ»Π½ΡΠΉ ΠΏΡΠΈΠΌΠ΅Ρ. ΠΠ°Π΄Π΅ΡΡΡ, ΡΡΠΎΠ³ΠΎ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ, ΡΡΠΎΠ±Ρ Π²Ρ Π½Π°ΡΠ°Π»ΠΈ.
Π‘ΠΏΠ°ΡΠΈΠ±ΠΎ, ΡΡΠΎ ΠΎΡΠ΅Π½Ρ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ. ΠΡΠΈΡΡΠ½ΠΎ ΠΈΠΌΠ΅ΡΡ ΠΏΡΠΈΠΌΠ΅Ρ, Π½Π° ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΡΠ»Π°ΡΡΡΡ.
ΠΠ»Ρ ΡΠ΅Ρ , ΠΊΡΠΎ ΠΏΡΡΠ°Π΅ΡΡΡ Π·Π°ΡΡΠ°Π²ΠΈΡΡ ΡΡΠΎ ΡΠ°Π±ΠΎΡΠ°ΡΡ, ΠΌΠ½Π΅, Π½Π°ΠΊΠΎΠ½Π΅Ρ, ΡΠ΄Π°Π»ΠΎΡΡ Π·Π°ΡΡΠ°Π²ΠΈΡΡ ΡΡΠΎ-ΡΠΎ ΡΡΠ½ΠΊΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°ΡΡ Π΄ΠΎΠ»ΠΆΠ½ΡΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ. ΠΠ°Π΄Π΅ΡΡΡ, ΡΡΠΎ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ Π»ΡΠ±ΠΎΠΌΡ, ΠΊΡΠΎ ΡΠ°ΠΊ ΠΆΠ΅ Π·Π°ΡΡΡΡΠ» ΠΈ ΡΠ°Π·ΠΎΡΠ°ΡΠΎΠ²Π°Π½, ΠΊΠ°ΠΊ ΠΈ Ρ:
conn.go:
package main
import (
"log"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Maximum message size allowed from peer.
maxMessageSize = 512
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// connection is an middleman between the websocket connection and the hub.
type connection struct {
// The websocket connection.
ws *websocket.Conn
// Buffered channel of outbound messages.
send chan []byte
}
// readPump pumps messages from the websocket connection to the hub.
func (s subscription) readPump() {
c := s.conn
defer func() {
h.unregister <- s
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, msg, err := c.ws.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
log.Printf("error: %v", err)
}
break
}
m := message{msg, s.room}
h.broadcast <- m
}
}
// write writes a message with the given message type and payload.
func (c *connection) write(mt int, payload []byte) error {
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
return c.ws.WriteMessage(mt, payload)
}
// writePump pumps messages from the hub to the websocket connection.
func (s *subscription) writePump() {
c := s.conn
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.ws.Close()
}()
for {
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.CloseMessage, []byte{})
return
}
if err := c.write(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
// serveWs handles websocket requests from the peer.
func serveWs(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
vars := mux.Vars(r)
log.Println(vars["room"])
if err != nil {
log.Println(err)
return
}
c := &connection{send: make(chan []byte, 256), ws: ws}
s := subscription{c, vars["room"]}
h.register <- s
go s.writePump()
s.readPump()
}
hub.go:
package main
type message struct {
data []byte
room string
}
type subscription struct {
conn *connection
room string
}
// hub maintains the set of active connections and broadcasts messages to the
// connections.
type hub struct {
// Registered connections.
rooms map[string]map[*connection]bool
// Inbound messages from the connections.
broadcast chan message
// Register requests from the connections.
register chan subscription
// Unregister requests from connections.
unregister chan subscription
}
var h = hub{
broadcast: make(chan message),
register: make(chan subscription),
unregister: make(chan subscription),
rooms: make(map[string]map[*connection]bool),
}
func (h *hub) run() {
for {
select {
case s := <-h.register:
connections := h.rooms[s.room]
if connections == nil {
connections = make(map[*connection]bool)
h.rooms[s.room] = connections
}
h.rooms[s.room][s.conn] = true
case s := <-h.unregister:
connections := h.rooms[s.room]
if connections != nil {
if _, ok := connections[s.conn]; ok {
delete(connections, s.conn)
close(s.conn.send)
if len(connections) == 0 {
delete(h.rooms, s.room)
}
}
}
case m := <-h.broadcast:
connections := h.rooms[m.room]
for c := range connections {
select {
case c.send <- m.data:
default:
close(c.send)
delete(connections, c)
if len(connections) == 0 {
delete(h.rooms, m.room)
}
}
}
}
}
}
Π‘Π°ΠΌΡΠΉ ΠΏΠΎΠ»Π΅Π·Π½ΡΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΉ
ΠΠ»Ρ ΡΠ΅Ρ , ΠΊΡΠΎ ΠΏΡΡΠ°Π΅ΡΡΡ Π·Π°ΡΡΠ°Π²ΠΈΡΡ ΡΡΠΎ ΡΠ°Π±ΠΎΡΠ°ΡΡ, ΠΌΠ½Π΅, Π½Π°ΠΊΠΎΠ½Π΅Ρ, ΡΠ΄Π°Π»ΠΎΡΡ Π·Π°ΡΡΠ°Π²ΠΈΡΡ ΡΡΠΎ-ΡΠΎ ΡΡΠ½ΠΊΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°ΡΡ Π΄ΠΎΠ»ΠΆΠ½ΡΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ. ΠΠ°Π΄Π΅ΡΡΡ, ΡΡΠΎ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ Π»ΡΠ±ΠΎΠΌΡ, ΠΊΡΠΎ ΡΠ°ΠΊ ΠΆΠ΅ Π·Π°ΡΡΡΡΠ» ΠΈ ΡΠ°Π·ΠΎΡΠ°ΡΠΎΠ²Π°Π½, ΠΊΠ°ΠΊ ΠΈ Ρ:
conn.go:
hub.go: