技術メモ

技術メモ

ラフなメモ

GolangでWebSocketを使ったHTTPサーバを実装する

はじめに

GolangでWebSocketを使ったHTTPサーバを構築します。

WebSocketの仕様は RFC6455 で定められています。Webアプリケーションにおいて、双方向通信を実現するための技術規格です。Ajaxではブラウザからサーバにデータの送信要求を出す手段でした。WebSocketではサーバからのデータをプッシュすることが可能になります。

GolangでWebSocketを扱うには golang.org/x/net/WebSocket パッケージを用いることができます。他にも存在します。

シンプルに実装することができて、type Handler func(*Conn)func(*Conn) を実装すればよいです。

実装

package main

import (
    "fmt"
    "log"
    "math/rand"
    "net/http"
    "strconv"
    "time"

    "golang.org/x/net/WebSocket"
)

var dataCh chan Data

type Data struct {
    MyData string `json:"myData"`
}

func main() {
    dataCh = make(chan Data, 1)

    http.HandleFunc("/", plotHandle)
    http.Handle("/data", WebSocket.Handler(dataHandler))
    err := http.ListenAndServe(":8888", nil)
    if err != nil {
        log.Fatal(err)
    }
}

func plotHandle(w http.ResponseWriter, r *http.Request) {
    go func() {
        for {
            dataCh <- Data{strconv.Itoa(rand.Int())}
            time.Sleep(500 * time.Millisecond)
        }
    }()
    fmt.Fprintf(w, page)
}

func dataHandler(ws *WebSocket.Conn) {
    for data := range dataCh {
        err := WebSocket.JSON.Send(ws, data)
        if err != nil {
            log.Printf("error sending data: %v\n", err)
            return
        }
    }
}

const page = `
<html>
  <head>
      <title>Hello WebSocket</title>

      <script type="text/javascript">
      var sock = null;
      var myData = "";
      function update() {
          var p1 = document.getElementById("my-data-plot");
          p1.innerHTML = myData;
      };
      window.onload = function() {
          sock = new WebSocket("ws://"+location.host+"/data");
          sock.onmessage = function(event) {
              var data = JSON.parse(event.data);
              myData = data.myData;
              update();
          };
      };
      </script>
  </head>
  <body>
      <div id="header">
          <h1>Hello WebSocket</h1>
      </div>
      <div id="content">
          <div id="my-data-plot"></div>
      </div>
  </body>
</html>
`

上記の実装はデータの送信にチャネルを用いていますが、以下のようにチャネルを使わなくても問題ないです。

WebSocket.JSON.Send(ws, Data{strconv.Itoa(rand.Int())})

ブラウザで確認すると以下のように数字列が更新されていくのが分かります。wscat などを使って確認することもできます。

f:id:tutuz:20191020214206p:plain

Tips

WebSocketそのものについては以下がわかりやすそうです。