Websocket: Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‡Π°Ρ‚-ΠΊΠΎΠΌΠ½Π°Ρ‚

Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹ΠΉ Π½Π° 6 нояб. 2014  Β·  3ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ  Β·  Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: gorilla/websocket

ΠŸΡ€ΠΈΠ²Π΅Ρ‚, ΠΌΠ½Π΅ Π±Ρ‹Π»ΠΎ интСрСсно, ΠΊΠ°ΠΊ я Π±ΡƒΠ΄Ρƒ Π΄Π΅Π»Π°Ρ‚ΡŒ Β«ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹Β» соСдинСний с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ gorilla websocket.
Π― установил простой Ρ‡Π°Ρ‚-сСрвСр, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ транслируСт сообщСния всСм, Π½ΠΎ ΠΌΠ½Π΅ Π±Ρ‹Π»ΠΎ интСрСсно, ΠΊΠ°ΠΊ Π±Ρ‹ я Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π» ΠΊΠΎΠΌΠ½Π°Ρ‚Ρ‹.

Π‘Π°ΠΌΡ‹ΠΉ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Для Ρ‚Π΅Ρ…, ΠΊΡ‚ΠΎ пытаСтся Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ это Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ, ΠΌΠ½Π΅, Π½Π°ΠΊΠΎΠ½Π΅Ρ†, ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ. НадСюсь, это ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π»ΡŽΠ±ΠΎΠΌΡƒ, ΠΊΡ‚ΠΎ Ρ‚Π°ΠΊ ΠΆΠ΅ застрял ΠΈ Ρ€Π°Π·ΠΎΡ‡Π°Ρ€ΠΎΠ²Π°Π½, ΠΊΠ°ΠΊ ΠΈ я:

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)
                    }
                }
            }
        }
    }
}

ВсС 3 ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’ΠΎΡ‚ нСкомпилированная ΠΈ нСпровСрСнная модификация Ρ…Π°Π±Π° ΠΈΠ· ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° 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)
                    }
                }
            }
        }
    }
}
Π‘Ρ‹Π»Π° Π»ΠΈ эта страница ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ?
0 / 5 - 0 Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΈ