WebSocket в Go
WebSocket даёт постоянное двустороннее соединение поверх HTTP upgrade. Подходит для чатов, live-дашбордов, игр и push-уведомлений, где REST с polling слишком медленный. Транспорт ниже HTTP — в TCP и UDP, основа HTTP — веб на stdlib.
См. также: WebSocket — обзор · Асинхронность.
WebSocket, HTTP и TCP
| Протокол | Модель | Типичное применение |
|---|---|---|
| REST/HTTP | Запрос → ответ | CRUD API |
| WebSocket | Долгое двустороннее | Чат, тикеры, совместное редактирование |
| Raw TCP | Произвольные байты | Свой бинарный протокол |
WebSocket начинается как обычный HTTP GET с заголовками Upgrade: websocket и Connection: Upgrade. После handshake соединение остаётся открытым — фреймы текста или бинарных данных идут в обе стороны.
Библиотека
Стандартная библиотека не содержит полноценного WebSocket-стека. На практике используют github.com/gorilla/websocket или nhooyr.io/websocket. Ниже — gorilla.
go get github.com/gorilla/websocket
Upgrader и сервер
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
// в проде — whitelist Origin, не return true всегда
return r.Host == "localhost:8080"
},
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer conn.Close()
for {
mt, msg, err := conn.ReadMessage()
if err != nil {
break
}
log.Printf("recv type=%d len=%d", mt, len(msg))
if err := conn.WriteMessage(mt, msg); err != nil {
break
}
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Разбор:
Upgrader.Upgradeпереводит HTTP-соединение в WebSocket; до upgrade ответ уже нельзя писать через обычныйResponseWriter.CheckOriginзащищает от CSRF-подобных подключений с чужих сайтов — в dev часто ослабляют, в prod проверяютOrigin.ReadMessage/WriteMessageработают с целыми фреймами; для потоковой обработки —NextReader/NextWriter.- Цикл завершается при закрытии клиентом или сетевой ошибке;
defer conn.Close()освобождает ресурсы.
WebSocket в браузере идёт как wss:// поверх TLS. Сервер поднимают через ListenAndServeTLS или reverse proxy (nginx, Caddy), который проксирует Upgrade.
Hub — рассылка нескольким клиентам
Типичный паттерн hub: центральная горутина регистрирует клиентов и рассылает broadcast.
type hub struct {
clients map[*websocket.Conn]struct{}
broadcast chan []byte
register chan *websocket.Conn
unregister chan *websocket.Conn
}
func (h *hub) run() {
for {
select {
case c := <-h.register:
h.clients[c] = struct{}{}
case c := <-h.unregister:
delete(h.clients, c)
c.Close()
case msg := <-h.broadcast:
for c := range h.clients {
if err := c.WriteMessage(websocket.TextMessage, msg); err != nil {
delete(h.clients, c)
c.Close()
}
}
}
}
}
Разбор:
- Одна goroutine-hub сериализует доступ к
clients— без mutex. register/unregisterвызывают из HTTP-handler послеUpgrade.- При ошибке записи клиент удаляют из map, иначе hub «залипнет» на мёртвом соединении.
- Broadcast через канал отделяет producers (HTTP, другие goroutine) от рассылки.
Связь с select и каналами: hub — классический event loop на select.
Клиент (Go)
conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
if err := conn.WriteMessage(websocket.TextMessage, []byte("hello")); err != nil {
log.Fatal(err)
}
_, msg, err := conn.ReadMessage()
Разбор:
DefaultDialer.Dialвыполняет client-side handshake.- Таймауты и TLS настраивают через поля
Dialer(HandshakeTimeout,TLSClientConfig). - Для нагрузочных клиентов переиспользуют одно соединение, а не Dial на каждое сообщение.
Ping, pong и дедлайны
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
conn.SetPongHandler(func(string) error {
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
Разбор:
- Без read deadline «тихие» клиенты держат goroutine и file descriptor.
- Ping/pong (RFC 6455) обнаруживает обрыв; gorilla может слать ping из фоновой goroutine.
- Паттерн совпадает с
SetDeadlineна TCP — см. 27.md.
WebSocket и горутины
На каждое соединение обычно одна goroutine на чтение и одна на запись (или hub). Ограничивайте число клиентов семафором — как для TCP-серверов.
При shutdown сервиса закрывают listener, затем все websocket.Conn, чтобы goroutine завершились до exit.
Ключевые тезисы
- WebSocket — upgrade HTTP, дальше фреймы, а не новый TCP-port.
CheckOrigin, TLS и лимиты соединений обязательны в production.- Hub на каналах масштабирует broadcast без гонок на map клиентов.
Мини-практикум
- Echo-сервер
/wsи клиент изwebsocket.DefaultDialer. - Добавьте hub с broadcast «новое сообщение всем».
- Сравните задержку push через WebSocket и polling REST раз в секунду.
Типичные ошибки
| Ошибка | Последствие |
|---|---|
CheckOrigin: true в prod | Подключение с чужого сайта |
Писать в ResponseWriter после Upgrade | Сломанный протокол |
| Нет ping/deadline | Утечка goroutine |
Concurrent WriteMessage без mutex | Race, corrupted frames |
Связанные материалы
- TCP и UDP в Go — UNIX domain sockets, сырой транспорт
- Веб на stdlib — HTTP до upgrade
- Gin — REST рядом с WebSocket на том же порту через
http.Server
Обзор WebSocket в сетевом разделе — WebSocket.
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Основы языка Go - философия простоты, модель компиляции и идиоматичный подход к системной разработке. Go — это статически типизированный язык программирования общего назначения, разработанный компанией Google для создания эффективных, масштабируемых и надежных систем. Набор советов, правил, принципов и обычаев в разработке на этом языке. История Go - инженерные цели языка, философия простоты и эволюция инструментов экосистемы. Экосистема приложений на Go - встроенные инструменты, workflow разработки и практики сопровождения проектов. Кавычки, rune и string, точка, запятая, автоматическая вставка точки с запятой, скобки, подчёркивания и типичные ошибки новичков в Go. Предопределённые идентификаторы не являются ключевыми словами, но имеют специальное значение в языке. Их можно переопределить в локальной области видимости, но делать это не рекомендуется. Набор функций, которые включены в стандартную библиотеку языка. Особенности Go - интерфейсы, композиция, модель ошибок и практики написания поддерживаемого кода. Go вводит конкурентность через встроенные синтаксические конструкции и правила выполнения. Ниже рассматриваются основные направления практического применения Go, объяснённые через призму его технических характеристик и требований реальных инфраструктур. Типизация, набор правил определения типа данных значений языка.Основы языка Go
Что требуется знать перед началом изучения языка программирования Go
Рекомендации по разработке на Go
История языка Go
Экосистема приложений на Go
Синтаксис и пунктуация в Go
Ключевые слова языка Go
Встроенные функции и пакеты Go
Особенности языка Go
Синтаксические конструкции Go
Области применения Go
Типы данных и объявление переменных в Go