見出し画像

GO言語のWebアプリフレームワークFiberで Websocketをまじめに使う方法

今朝は6時まで寝てしまいました。ときがわの温泉水で沸かしたお風呂の効果です。
本業でGO言語のWebアプリーフレームワークFiberを使っています。

普通のリクエストを処理する部分は簡単できましたが、開発しているものは
WebSocketも使う必要があって、そこで調べたことを書いておきます。
FiberのトップページにもWebScoketの例が書いていあります。

app.Get("/ws", websocket.New(func(c *websocket.Conn) {
  // Websocket logic
  for {
    mtype, msg, err := c.ReadMessage()
    if err != nil {
      break
    }
    log.Printf("Read: %s", msg)

    err = c.WriteMessage(mtype, msg)
    if err != nil {
      break
    }
  }
  log.Println("Error:", err)
}))

です。実は、これを見て感動してFiberを使う気になりました。
上のサンプルプログラムだと受信したデータを、そのまま送信しているだけなので、実際に私の作りたいものに組み込もうと思うと、いくつも疑問がでてきます。

  • 受信データがないと処理が止まってしまうのでは?

  • 受信したデータを他の処理にどうやって送る?

  • 自分から送りたい時はどうするの?

などなどです。
サンプルは簡単だけど、それを応用する方法で悩むことはかなりあります。

日曜日にキャンプ講習でリフレッシュして昨日は頭もスッキリしていたので、まじめに調べました。
トップページのサンプルの後に、

のドキュメントを見ました。でも、なんとなく説明に記載されている関数とかが少ない。不思議に思ってGitHUBのページ

も見てみました。

Based on Fasthttp WebSocket for Fiber with available *fiber.Ctx methods like Locals, Params, Query and Cookies.

gofiber/websocket

とありました。FasthttpのWebSocketをベースにしていたのです。
ベースにしたパッケージは

です。このページには、ちゃんとしたドキュメントへのリンクや
サンプルプログラムのリンクがありました。
APIのドキュメントは

です。
そして、私がトップページのサンプルで思ったすべての疑問を解決してくれるサンプルプログラムもありました。

です。

このな感じのサンプルです。ポイントはHUBです。

/ Hub maintains the set of active clients and broadcasts messages to the
// clients.
type Hub struct {
	// Registered clients.
	clients map[*Client]bool

	// Inbound messages from the clients.
	broadcast chan []byte

	// Register requests from the clients.
	register chan *Client

	// Unregister requests from clients.
	unregister chan *Client
}

func newHub() *Hub {
	return &Hub{
		broadcast:  make(chan []byte),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		clients:    make(map[*Client]bool),
	}
}

func (h *Hub) run() {
	for {
		select {
		case client := <-h.register:
			h.clients[client] = true
		case client := <-h.unregister:
			if _, ok := h.clients[client]; ok {
				delete(h.clients, client)
				close(client.send)
			}
		case message := <-h.broadcast:
			for client := range h.clients {
				select {
				case client.send <- message:
				default:
					close(client.send)
					delete(h.clients, client)
				}
			}
		}
	}
}

短いソースコードですが、接続したクライアントを登録したり、切断したら登録解除したり、他のクライアントに送信したりできます。

このソースコードを発見した時には、かなり幸せな気分になりました。
本業のプログラムに組み込んで快適に動作しています。

明日に続く


開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。