Простейший сервер на Gorilla WebSocket

от автора

В этом небольшом туториале, мы чуть подробнее разберем использование Gorilla WebSocket для написания своего websocket сервера, на примере чуть более функциональном, чем базовый пример и более легком для понимания, чем пример чата.

Что будет уметь наш сервер?

  1. Отправлять новые сообщения от клиентов в callback

  2. Хранить активные соединения и закрывать/удалять не активные

  3. Рассылать сообщения по активным соединениям

Для начала поднимем обычный http сервер при помощи net/http, для того чтобы мы могли отлавливать запросы на соединение:

package main  import ( "fmt" "html" "net/http" )  func main() { http.HandleFunc("/", echo) http.ListenAndServe(":8080", nil) }  func echo(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }

Теперь научим его «апгрейдить» соединение:

import ( "github.com/gorilla/websocket" "net/http" )  var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true // Пропускаем любой запрос }, }  func echo(w http.ResponseWriter, r *http.Request) { connection, _ := upgrader.Upgrade(w, r, nil) connection.Close() // Закрываем соединение }

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

func echo(w http.ResponseWriter, r *http.Request) { connection, _ := upgrader.Upgrade(w, r, nil)  for { _, message, _ := connection.ReadMessage()      connection.WriteMessage(websocket.TextMessage, message) go messageHandler(message) } }  func messageHandler(message []byte)  {   fmt.Println(string(message)) }

Научим наш сервер закрывать соединение:

func echo(w http.ResponseWriter, r *http.Request) { connection, _ := upgrader.Upgrade(w, r, nil)  for { mt, message, err := connection.ReadMessage()  if err != nil || mt == websocket.CloseMessage { break // Выходим из цикла, если клиент пытается закрыть соединение или связь с клиентом прервана }  connection.WriteMessage(websocket.TextMessage, message)  go messageHandler(message) }  connection.Close() }

Чтобы иметь возможность рассылать сообщения по разным соединениям, нам нужно где то их хранить, в нашем случае подойдет простейший map:

var clients map[*websocket.Conn]bool  func echo(w http.ResponseWriter, r *http.Request) { connection, _ := upgrader.Upgrade(w, r, nil)  clients[connection] = true // Сохраняем соединение, используя его как ключ  for { mt, message, err := connection.ReadMessage()  if err != nil || mt == websocket.CloseMessage { break // Выходим из цикла, если клиент пытается закрыть соединение или связь прервана }  // Теперь мы рассылаем сообщения всем клиентам go writeMessage(message)  go messageHandler(message) }  delete(clients, connection) // Удаляем соединение  connection.Close() }  func writeMessage(message []byte) { for conn := range clients { conn.WriteMessage(websocket.TextMessage, message) } }

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

package ws  import ( "github.com/gorilla/websocket" "net/http" )  var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true // Пропускаем любой запрос }, }  type Server struct { clients       map[*websocket.Conn]bool handleMessage func(message []byte) // хандлер новых сообщений }  func StartServer(handleMessage func(message []byte)) *Server { server := Server{ make(map[*websocket.Conn]bool), handleMessage, }  http.HandleFunc("/", server.echo) go http.ListenAndServe(":8080", nil) // Уводим http сервер в горутину   return &server }  func (server *Server) echo(w http.ResponseWriter, r *http.Request) { connection, _ := upgrader.Upgrade(w, r, nil)  server.clients[connection] = true // Сохраняем соединение, используя его как ключ  for { mt, message, err := connection.ReadMessage()  if err != nil || mt == websocket.CloseMessage { break // Выходим из цикла, если клиент пытается закрыть соединение или связь прервана }  go server.handleMessage(message) }  delete(server.clients, connection) // Удаляем соединение  connection.Close() }  func (server *Server) WriteMessage(message []byte) { for conn := range server.clients { conn.WriteMessage(websocket.TextMessage, message) } }
package main  import ( "fmt" "simple-webcoket/ws" )  func main() { server := ws.StartServer(messageHandler)  for { server.WriteMessage([]byte("Hello")) } }  func messageHandler(message []byte) { fmt.Println(string(message)) }

Теперь у нас есть реализация простейшего webscoket сервера, который способен принимать и рассылать сообщения по активным соединениям.


ссылка на оригинал статьи https://habr.com/ru/post/599737/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *