Следите за новинками
в нашем Telegram канале. Жми, чтобы подписаться:)
⊗goSpWsSC 265 of 280 menu
◀ ▶

Несколько WebSocket-соединений в Go

Один HTTP-сервер может держать много каналов одновременно. Если после перевода запроса долго читать сообщения в том же потоке, следующий клиент будет ждать, пока не освободится обработчик пути.

Поэтому каждое новое соединение часто кладут в карту под sync.Mutex, чтобы не потерять указатель на канал, а длинную работу выносят в горутину. Перед записью в карту замок берут, после изменения отпускают; горутины и замок уже знакомы из раздела про синхронизацию.

Сервер добавляет каждый канал в карту и читает одно сообщение в отдельной ветке:

package main import ( "log" "net/http" "sync" "github.com/gorilla/websocket" ) type hub struct { mu sync.Mutex conns map[*websocket.Conn]struct{} } func (h *hub) add(conn *websocket.Conn) { h.mu.Lock() h.conns[conn] = struct{}{} h.mu.Unlock() } func (h *hub) remove(conn *websocket.Conn) { h.mu.Lock() delete(h.conns, conn) h.mu.Unlock() } func handleClient(h *hub, conn *websocket.Conn) { defer conn.Close() defer h.remove(conn) _, msg, err := conn.ReadMessage() if err != nil { return } log.Println("got", string(msg)) } func main() { h := &hub{conns: make(map[*websocket.Conn]struct{})} upgrader := websocket.Upgrader{} http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } h.add(conn) go handleClient(h, conn) }) log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil)) }

Карта хранит только активные каналы. Удаление в отложенном вызове обработчика не дает копиться закрытым соединениям, если клиент оборвет связь.

Тот же прием карты без чтения сообщения, только регистрация и запуск ветки:

package main import ( "log" "net/http" "sync" "github.com/gorilla/websocket" ) var ( mu sync.Mutex conns = map[*websocket.Conn]struct{}{} ) func track(conn *websocket.Conn) { mu.Lock() conns[conn] = struct{}{} mu.Unlock() go func(c *websocket.Conn) { defer c.Close() defer func() { mu.Lock() delete(conns, c) mu.Unlock() }() for { if _, _, err := c.ReadMessage(); err != nil { return } } }(conn) } func main() { upgrader := websocket.Upgrader{} http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } track(conn) }) log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil)) }

Дана карта и замок:

var ( mu sync.Mutex conns = map[*websocket.Conn]struct{}{} )

Напишите функцию, которая под замком добавляет conn в conns и запускает горутину с отложенным удалением из карты.

Дан фрагмент обработчика пути:

conn, err := upgrader.Upgrade(w, r, nil) if err != nil { return } handleClient(conn)

Скажите, чем такой вызов хуже для второго клиента, пока первый еще читает сообщения.

Скажите, зачем карту клиентов защищают замком, если добавление и удаление идут из разных горутин.

← →
↑
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить