reactjs+Flaskでwebsocket通信
概要
フロントエンド(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
Flaskは、プログラミング言語Python用の、軽量なウェブアプリケーションフレームワークである。標準で提供する機能を最小限に保っているため、「マイクロフレームワーク」と呼ばれている。
- flask-cors
Cross Origin Resource Sharing (CORS) を扱うためのFlask拡張で、クロスオリジンAJAXを可能にする。
- flask-socketio
Flask-SocketIOは、Flaskアプリケーションに、クライアントとサーバ間の低レイテンシ双方向通信へのアクセスを提供します。クライアント側のアプリケーションは、Javascript、Python、C++、Java、SwiftのいずれかのSocketIOクライアントライブラリ、またはその他の互換性のあるクライアントを使用して、サーバとの恒久的な接続を確立することができます。
-
on関数
SocketIO イベントハンドラを登録するためのデコレータです。
https://flask-socketio.readthedocs.io/en/latest/api.html#flask_socketio.SocketIO.on -
emit関数
emit()関数を用いて、接続したクライアントに返信メッセージを送信します。
https://flask-socketio.readthedocs.io/en/latest/api.html#flask_socketio.SocketIO.emit
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
React は、インタラクティブなユーザインターフェイスの作成にともなう苦痛を取り除きます。アプリケーションの各状態に対応するシンプルな View を設計するだけで、React はデータの変更を検知し、関連するコンポーネントだけを効率的に更新、描画します。
- Socket.IO
Socket.IOは、クライアントとサーバー間で低遅延、双方向、イベントベースの通信を可能にするライブラリです。
Test
それでは早速フロントエンドのサーバーとバックエンドのサーバーを起動してみましょう。起動すると以下のような画面が ブラウザに表示されます。
文字を入力し、「submit」すると以下のように画面をサーバサイドから値がレスポンスされて描画されます。
まとめ
Websocket通信を使用したフロントエンドとバックエンドの構成でアプリを作成してみました。作成した環境はローカルで実行するアプリを作成したが、Dockerにも載せられるのでこれを次回はDockerへと!
Discussion