💬

reactjs+Flaskでwebsocket通信

2023/05/01に公開約5,900字

概要

フロントエンド(ReactJS)とバックエンド(Flask)をそれぞれ構築し、Websocket通信してみます。
フロントエンドから何か送信したらその送信した文字列に文字を追加してフロントエンドに送信し、画面に描画するアプリを構築します。つまり、フロントエンドからのリクエストをトリガーとしてバックエンドではフロントエンドに文字列を送信します。

ディレクトリ

.
├─ be
    ├─ app.py
    ├─ requirements.txt
    └─ socket_manager.py
├─ fe
    ├─ App.js
    ├─ ...
    ├─ socketio.js
    ├─ websocketForm.js
    ├─ ...
    └─ package.json

Flask

Flaskを使用して最小構成でWebsocketサーバーを構築します。フロントエンドからのリクエストをトリガーとしてフロントエンドに文字列を追加して送信する処理を作成します。

環境

  • python 3.11

ライブラリ

requirements.txt

bidict==0.22.1
blinker==1.6.2
click==8.1.3
Flask==2.3.1
Flask-Cors==3.0.10
Flask-SocketIO==5.3.3
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.2
python-engineio==4.4.1
python-socketio==5.8.0
six==1.16.0
Werkzeug==2.3.2

サーバー構築

サーバー側のソースの内容は以下の通りです。おそらく最小構成で作成できていると思います。

  • app.py
from flask import Flask, render_template
from flask_socketio import SocketIO
from flask_cors import CORS

from socket_manager import SocketManager

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
CORS(app)
socketio = SocketIO(app, cors_allowed_origins='*')
socket = SocketManager()

# とりあえず、サーバーに接続できるかのテスト用
@app.get('/')
def index():
    return {"status": 200}


@socketio.on('connect')
def connect(auth):
    socket.connect()

@socketio.on('disconnect')
def disconnect():
    socket.disconnect()

@socketio.on('websocket-test')
def update_text(text):
    print(text)
    socket.update_text(text)

if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=8000, debug=True)
  • socket_manager.py

接続に成功したらコンソール上にconnectを出力し、クライアントからリエクエストがあった際にクライアントにメッセージを送信します。

from flask_socketio import emit

class SocketManager:

    def __init__(self):
        pass

    def connect(self):
        print('connect')

    def disconnect(self):
        print('disconnect')

    def update_text(self, text):
        # emit()関数を用いて、接続したクライアントに返信メッセージを送信する
        emit('websocket-test', {'text': f'server: {text}'}, broadcast=True)
  • flask

https://flask.palletsprojects.com/en/2.3.x/

Flaskは、プログラミング言語Python用の、軽量なウェブアプリケーションフレームワークである。標準で提供する機能を最小限に保っているため、「マイクロフレームワーク」と呼ばれている。

  • flask-cors

https://flask-cors.readthedocs.io/en/latest/

Cross Origin Resource Sharing (CORS) を扱うためのFlask拡張で、クロスオリジンAJAXを可能にする。

  • flask-socketio

https://flask-socketio.readthedocs.io/en/latest/

Flask-SocketIOは、Flaskアプリケーションに、クライアントとサーバ間の低レイテンシ双方向通信へのアクセスを提供します。クライアント側のアプリケーションは、Javascript、Python、C++、Java、SwiftのいずれかのSocketIOクライアントライブラリ、またはその他の互換性のあるクライアントを使用して、サーバとの恒久的な接続を確立することができます。

React

次にフロントエンド側を構築します。基本的にはReactJSのチュートリアル通り最初は構築します。なので割愛!(詳しくはこちら

ライブラリのインストール

socket.ioはWebsocketやHTTPポーリング等、適切な接続を自動的に切り替えて双方向通信を実現してくれるライブラリです。ocket.io-clientはsocket.ioでサーバへ接続するためのクライアント用ライブラリです。

npm install socket.io-client

フロントエンド構築

今回はApp.jsと同じ階層に全て作成していきます。先ほどReactJSのチュートリアルより作成したプロジェクトに修正を加えていきます。修正については以下の内容を追加します。

  • socketio.js: websocket設定ファイル

今回はローカル環境で行なっているので以下のURLとします。

import { io } from 'socket.io-client';

const URL = 'http://127.0.0.1:8000';
export const socket = io(URL);
  • websocketForm.js: 画面のコンポーネント

テキストフォームとSubmitボタンといった単純な構成にしております。

import React, { useState } from 'react';
import { socket } from './socketio';

export const WebsocketForm = () => {
  const [value, setValue] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const onSubmit = (event) => {
    event.preventDefault();
    setIsLoading(true);

    socket.timeout(5000).emit('websocket-test', value, () => {
      setIsLoading(false);
    });
  }

  return (
    <form onSubmit={ onSubmit }>
      <input onChange={ e => setValue(e.target.value) } />

      <button type="submit" disabled={ isLoading }>Submit</button>
    </form>
  );
}
  • App.js: Mainファイル

メインファイルでバックエンドのサーバーと接続を行います。また、サーバーからレスポンスされた値を箇条書きの容量で表示します。

import React, { useState, useCallback, useEffect } from 'react';
import { WebsocketForm } from './websocketForm';
import { socket } from './socketio';
import './App.css';

const App = () => {
  const [isConnected, setIsConnected] = useState(socket.connected);
  const [messages, setMessage] = useState([]);

  useEffect(() => {
    const onConnect = () => {
      setIsConnected(true);
    }

    const onDisconnect = () => {
      setIsConnected(false);
    }

    const onFooEvent = (message) => {
      setMessage(messages => [...messages, message.text]);
    }

    socket.on('connect', onConnect);
    socket.on('disconnect', onDisconnect);
    socket.on('websocket-test', onFooEvent);

    return () => {
      socket.off('connect', onConnect);
      socket.off('disconnect', onDisconnect);
      socket.off('websocket-test', onFooEvent);
    };
  }, []);

  return (
    <div>
      <h1>Your Websocket App</h1>
      <p>State: { '' + isConnected };</p>
      <ul>
      {
        messages.map((event, index) =><li key={ index }>{ event }</li>)
      }
      </ul>
      <WebsocketForm />
    </div>
  );
}

export default App;
  • ReactJS

https://ja.legacy.reactjs.org/

React は、インタラクティブなユーザインターフェイスの作成にともなう苦痛を取り除きます。アプリケーションの各状態に対応するシンプルな View を設計するだけで、React はデータの変更を検知し、関連するコンポーネントだけを効率的に更新、描画します。

  • Socket.IO

https://socket.io/docs/v4/client-api/

Socket.IOは、クライアントとサーバー間で低遅延、双方向、イベントベースの通信を可能にするライブラリです。

Test

それでは早速フロントエンドのサーバーとバックエンドのサーバーを起動してみましょう。起動すると以下のような画面が ブラウザに表示されます。

websocket-webapp-disp

文字を入力し、「submit」すると以下のように画面をサーバサイドから値がレスポンスされて描画されます。

まとめ

Websocket通信を使用したフロントエンドとバックエンドの構成でアプリを作成してみました。作成した環境はローカルで実行するアプリを作成したが、Dockerにも載せられるのでこれを次回はDockerへと!

Discussion

ログインするとコメントできます