[Go言語/JavaScript]ハンズオンで作る掲示板アプリ!~WebSocketsコネクション確立編~

こんにちは、 KOUKIです。

本記事では、掲示板アプリケーション開発のプロセスをハンズオン形式で記事にしています!

前回は、開発環境の準備、Webサーバー及び掲示板UIの実装を行いました。

今回は、WebSocketsを使って、コネクションの確立を行いたいと思います。

WebSocket API は、ユーザーのブラウザーとサーバー間で対話的な通信セッションを開くことができる先進技術です。この API によって、サーバーにメッセージを送信したり、応答をサーバーにポーリングすることなく、イベント駆動型のレスポンスを受信したりすることができます。

MDNより

前回

まとめページ

サーバー側の実装

WebSockets対応を行うため、以下の作業が必要になります。

実装に必要なこと 1. WebSocketsのエンドポイントを作成する
2. routeを作成する
3. WebSocketsコネクションを保持する仕組みを作る
4. WebSocketsをリッスンする
5. 投稿をブロードキャストする
6. payloadチャネルを別プロセスで読み込む

少々難しいですが、一つずつ段階を追っていけば理解できると思います^^

WebSocketsのエンドポイントを作成する

最初に、WebSocketsのエンドポイントを作成しましょう。

connect.goに、サーバー内で処理したデータをクライアント側へ返却するための構造体を実装します。

「Action」には、クライアント側で実行するアクションを指定します。例えば、ユーザーが離脱したことを知らせるために「left」アクションを送ったりします。そして、「Post」には、ユーザーの投稿を格納します。

次に、handlers.goにエンドポイントを実装します。

WebSocketsのエンドポイントを作成するために、Gorilla WebSocketを利用しました。これを利用するとWebSocket プロトコルを簡単に実装できます。

upgradeConnection変数には、WebSocketsプロトコルの基本設定を定義します。そして、この変数が提供する「Upgrade」メソッドで、HTTP RequestをWebSocketsへアップグレードします。

また、「CheckOrigin:     func(r *http.Request) bool { return true },」の設定がない場合は、「websocket: request origin not allowed by Upgrader.CheckOrigin」エラーが表示され、クライアントからの接続に失敗(WebSocket connection to ‘ws://127.0.0.1:8080/ws’ failed: )します。

routeを作成する

エンドポイントへリクエストを送る処理を実装しましょう。

routes.goに、エントリポイントを追加します。

この設定により、クライアントから「ws://127.0.0.1:8080/ws」へGETリクエストを送れるようになりました。

本アプリでは、画面を開いた時にサーバへWebSocketsコネクションを張りに行くので、GETを指定しています。

WebSocketsコネクションを保持する仕組みを作る

掲示板アプリでは、WebSocketsコネクションを保持する必要があります。コネクション情報を保持していないと掲示板にアクセスしている全てのユーザーに対して、投稿データをブロードキャストできないからです。

connect.goに次の構造体を追加します。

次に、handlers.goに以下の変数を定義します。

wsChan変数では、payloadチャネルを作成しています。

WebSocketsのリクエストは非同期で受信する必要があり、goroutineを使ってWebサーバーとは別のプロセスで動かします。

goroutine間でデータの送受信を行うには「チャネル」が必要となるため、ここに定義しました。

clients変数は、WebSocketsコネクション情報をkeyにしたMapを指定しています。このようにユーザー情報を格納しておけば、全ユーザーにブロードキャストでデータを送れるようにできます。

この変数は、ページを開いた時に以下のように格納されるべきです。

「WsEndpoint」エンドポイントは、ページを開いた時に一度だけ呼ばれます。その時に、コネクション情報をclients変数のKeyとして保持します。

ユーザー名については、ブラウザの「User Name」に名前を入れた時、非同期でWebSocketsリクエストを送り、格納したいと思います。

WebSocketsをリッスンする

次は、WebSocketsをリッスンする処理を実装します。

ListenForWs関数は、パラメータにWebSocketsのコネクション情報を持ちます。この情報の中にpayload(クライアントから送られてきたデータ)が格納されているので、「ReadJSON」メソッドで読み取り、wsChanチャネルに格納します。

また、この関数は、forループで永続的に回しています。無限ループで回して大丈夫なの?と思われるかもしれませんが、「ReadJSON」メソッドが新しいpayloadを読み込むまで後続処理をブロックするので問題ありません。

無限ループで動かすという特性上、ListenForWs関数を別プロセスで動作させましょう。そうしない場合は、mainプロセスをブロックしてしまうので、他のユーザーのリクエストを受け付けられなくなります。

WsEndpoint関数からgoroutineを使って、ListenForWs関数を呼び出します。

投稿をブロードキャストする

次は、ユーザーの投稿をブロードキャストする処理を実装します。

投稿をユーザーに返す処理は、「WriteJSON」メソッドで実現可能です。

前述の通り、clients変数には全てのユーザーのWebSocketsコネクションが格納されているので、forループで回して、WriteJSONメソッドでレスポンスを返せます。

この関数を使うためには、実はもう一つ関数を実装する必要があります。

先ほど、Websocketsをリッスンし、新しいpayloadがコネクションに入ってきたら、wsChan チャネルに格納したと思います。

このチャネルからデータを取り出すして、ブロードキャストに渡す必要があります。

以下の関数を実装しましょう。

ListenToWsChannel関数は、wsChanチャネルからpayloadを読み込み、任意のデータに加工後、broadcastToAllUser関数で全てのユーザーに対して、ブロードキャストを行います。

ここも無限ループで実装していますが、wsChanチャネルからデータを読み込むまで後続の処理をストップするので、これでOKです。

payloadチャネルを別プロセスで読み込む

ListenToWsChannel関数は、main関数とは別のプロセスで動かします。

main.goに以下の処理を追加します。

これで、合計3つのプロセスが立ち上がることになります。

  1. Webサーバーのプロセス
  2. WebSocketsをリッスンするプロセス
  3. wsChanチャネルをリッスンするプロセス

JavaScript側の実装

次は、JavaScript側の処理を実装していきましょう。

実装に必要なこと 1. WebSocketsオブジェクトを作成する
2. 接続を確立する

WebSocketsオブジェクトを作成する

WebSocketsオブジェクトの作り方は、公式ページに詳しく載っています。

これで、自動的にサーバーへの接続が開かれます。

接続を確立する

WebSocketsのonopenメソッドは、WebSocketsのコネクションの確立がなされた時に呼び出されます。つまり、データを送受信する準備ができたことを表します。

以下のコマンドでWebサーバーを起動し、ブラウザから「http://localhost:8080/」にアクセスしましょう。

「Successfully connectted!」メッセージが表示されたので、接続は問題ないですね。

WebSocketsコネクションの確立が完了したので、今回はここまでにします。

次回

次回は、掲示板の投稿機能を実装したいと思います。

Go記事まとめ

JavaScript記事まとめ

コメントを残す