Реализация websocket на Go

Введение

Иногда воникают задачи отобразить какие-либо изменения на клиенте, которые произошли на сервере, например при сборе телеметрии, отображать перемещение объекта или изменение его показателей. Для этой задачи часто используют протокол WebSocket.

В статье я хочу показать как реализовать websocket на Go, который, для реалистичности, будет отдавать GeoJSON.

Реализация

Для реализации веб сокета будут использоваться следующие библиотеки

  • Echo - минималистичный web фреймворк.
  • Websocket - Реализация протокола websocket на Go.
  • go.geojson - декодер GeoJSON на Go

Код сокета приведен ниже:

package main

import (
    "github.com/gorilla/websocket"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "github.com/labstack/gommon/log"
    "github.com/paulmach/go.geojson"
    "net/http"
    "time"
)

var upgrader = websocket.Upgrader{}

func main() {
    e := echo.New()
    e.Use(middleware.Logger())
    e.Pre(middleware.RemoveTrailingSlash())

    e.Logger.SetLevel(log.DEBUG)

    e.GET("/ws", geoWebsocket)

    e.Logger.Fatal(e.Start("localhost:8081"))
}

func geoWebsocket(c echo.Context) error {
    upgrader.CheckOrigin = func(r *http.Request) bool { return true }
    ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
    if err != nil {
        return err
    }
    defer ws.Close()

    geojsonPoint := `{
                       "type": "feature",
                       "geometry": {
                         "type": "point",
                         "coordinates": [
                           37.5334919,
                           55.6077987
                         ]
                       },
                       "properties": {
                         "client": "30457412",
                         "nph_request_id": "577341",
                         "nav_time": "2019-02-22t18:31:22.095480z"
                       }
                     }`

    point, err := geojson.UnmarshalFeature([]byte(geojsonPoint))
    if err != nil {
        c.Logger().Fatal(err)
    }

    for {
        if geoPkg, err := point.MarshalJSON(); err != nil {
            c.Logger().Error(err)
        } else {
            c.Logger().Debug("Send: ", string(geoPkg))

            if err := ws.WriteMessage(websocket.TextMessage, geoPkg); err != nil {
                c.Logger().Error(err)
            }
        }

        time.Sleep(1 * time.Second)
    }
}

В данном коде сначала создается экземпляр web приложения, затем к нему подключаются логгер, и middleware для удаления последнего слэша в адресе.

После этого на GET запрос привязвыется обработчк, который содержит сам websocket.

Затем мы разбираем строку с помощью geojson и каждую секунду отправляем эту точку в websocket.

Как видно код получился не самый сложный.

Проверить что веб сокет работает, можно через простенький html:

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>WebSocket</title>
</head>

<body>
<p id="output"></p>

<script>
    ws = new WebSocket('ws://localhost:8081/ws');

    ws.onopen = function() {
        console.log('Connected')
    };

    ws.onmessage = function(evt) {
        var out = document.getElementById('output');
        console.log(evt);
        out.innerHTML += evt.data + '<br>';
    };
</script>
</body>

</html>

Если запустить websoсket, а затем открыть в браузере html файл приведенный выше, то можно наблюдать следующее:

результат работы ws

Заключение

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

 
comments powered by Disqus