Reactサンプルアプリ開発 プラニングポーカー (13) Flask-SocketIOでWebSocket通信のグルーピングを行う

Poker サンプル開発

プラニングポーカーアプリでは、プレイヤーの参加、入札、オープンといった各種イベントをWebSocketによる双方向通信で行い、リアルタイム性を実現している。
ライブラリとしては、クライアント側(JavaScript)はsocket.io-clientを、サーバー(Flask-SocketIO)を利用している。

実はこれまでの実装には致命的な欠陥があった。1つの場を利用しているときはよいのだが、別のユーザーが別の場を開いた場合にそれぞれの場同士が干渉し合ってしまうのだ。これでは使いものにならない。

チャットアプリにおけるチャットルームのような、複数のクライアントのグルーピングを行って、WebSocketによる通信はグループ内に閉じるようにしたい。
実はFlask-SocketIOにはまさにroomという機能が存在するので、その使い方を検証した。

WebSocket通信の名前空間をルームの単位で分割する必要があるが、今回はまさに場(Table)がその単位となるべきである。プレイヤーが場に加わるタイミングで、そのルームに加わるようにする。
Flask-SocketIOからjoin_room関数をインポートしておく。

from flask_socketio import SocketIO, emit, join_room

場/ルームにジョインするためのエンドポイント。

@socketio.on("join")
def join(table_id):
    print(f"A player is joining {table_id}")
    join_room(table_id)

クライアントは場のIDは既知なので、joinイベントの引数としてtable_idを受け取り、join_room関数の引数として使用する(4行目)。

クライアント側から、上記のjoinイベントを送出するように実装。

  useEffect(() => {
    axios.get(`/table/${props.tableId}/players`)
      .then((res) => {
        const players = mapPlayers(res.data.players, props.playerId);
        console.log(`Got ${players.length} players by ajax call.`)
        setPlayers(players);
      }).then(() => {
        // ルームを指定してWebSocket接続するためにイベント送出
        console.log("Joining a room (web socket)")
        socket.emit("join", props.tableId);
      }).catch((error) => {
        console.log("通信失敗")
        console.log(error.status);
      });
    return () => {
      socket.close();
    }
  }, []);

上記は、コンポーネントの初回レンダリング時に一度だけ実行されるEffectである(useEffectの第2引数が空配列となっている点に注意)。8-10行目で、イベントを送出している。

これ以降、socketオブジェクトよるイベントの送受信はすべて同一のルーム内でのやり取りとなる。クライアント側では特にルームを意識する必要はないのだが、イベント送出時は必ずtable_idを引数にセットしてあげる必要がある。

例えば、次のゲームの開始イベント。

  const handleNewGame = () => {
    setTimeout(() => {
      socket.emit("newgame", props.tableId);
    }, 0);
  }

サーバー側は以下のようになる。

@socketio.on("newgame")
def handle_newgame(table_id):
    print(f"Starting a new game, broadcasting in {table_id}...")
    emit("newgame_begun", room=table_id, broadcast=True)

emit関数の名前付き引数roomに、ルームの識別子(この場合、table_id)を指定する(4行目)。

これでWebSocket通信がルームという論理的な単位でグルーピングできるようになった。

コメント

タイトルとURLをコピーしました