今回は、PythonでSocket.IOモジュールを使って、クライアントとサーバー間でコミュニケーションする方法を紹介したいと思います。webページの値を更新したり、webページから入力された文字等を取得できたりしますので、とても重宝します。それでは、クラアント側のプログラムについて説明してきます。
Socket.IOモジュールのインストール
下記コマンドでSocket.IOモジュールをインストールします。
$ pip install "python-socketio[client]"
asyncioでの使い方は今回は紹介しませんが、コードの一部を置き換えるだけで使えるようになります。興味がある方は、下記のSocket.IOモジュールのドキュメントを参考にして、試してみてください。
Socket.IOの基本
ここにSocket.IOモジュールのドキュメントがあります。参考にしてください。
python-socketio.readthedocs.io
サーバーとクライアントのコミュニケーションの仕方
データをサーバー⇒クライアント、またはサーバーからクライアントに送るときは、emit
という関数を使います。emitの第1引数と受信側の関数名を一致させて使う必要があります。
# 送信側;サーバーorクライアントでemit関数を使用してデータを送信する emit('response', data) # 受信側:サーバーorクライアントの'response’という関数名の関数が実行されます。 def response(data): print(data) ※emit関数(送信側)でイベント(受信側)を発生させることができる。emitの第1引数の名前のイベントが受信側で発生するので、上記コードのresponseという名前を一致させる必要がある。
イベントハンドラの登録
Socket.IOは双方向のイベントベースの通信プロトコルです。何かやりたい場合は、イベントハンドラを登録する必要があります。デコレータ(@sio.event)を使う方法とNamespacesクラスを使用してon_関数名で使う方法があります。
@sio.event # デコレータを使用する場合 def connect(sid, environ): # 処理 class MyCustomNamespace(socketio.Namespace): # Namespacesクラスを使用する場合 def on_connect(self, sid, environ): # 処理
名前空間(Namespace)
サーバーとクライアントで同じ名前空間(Namespace)を使わないと接続ができません。逆に接続を個別に行いたいときは名前空間を分けて設定します。下記のchatやtestの部分で名前空間を設定します。
http://192.168.0.1:3000/
http://192.168.0.1:3000/chat
http://192.168.0.1:3000/test
Socket.IOモジュールでは、デコレータを使う方法をNamespacesクラスを使う方法をがあります。コードの1部分になりますので、ざっとイメージを掴むだけで問題ありません。
@sio.event(namespace='/chat') def connect(sid, environ): # 処理 class MyCustomNamespace(socketio.Namespace): # Namespacesクラスを使用する場合 def on_connect(self, sid, environ): # 処理 sio.register_namespace(MyCustomNamespace('/chat'))
また、名前空間の中をさらにroomという機能で個別に管理することもできます。
それでは全体のコードを見ていきましょう。
Socket.IOクライアントのPythonコード
コード内にコメントも記載しましたので、参考にしてください。testという名前空間で、バックグラウンドタスクでemit関数でサーバーにメッセージを送信しています。また、サーバーからのレスポンスはon_response関数で受信するコードになっています。
・start_background_task(関数、引数) ⇒ 第一引数の関数がバックグラウンドタスクとして動作します。引数も渡すことができます。
import socketio import time from datetime import datetime class MyCustomNamespace(socketio.ClientNamespace): # 名前空間を設定するクラス def on_connect(self): print('[{}] connect'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) def on_disconnect(self): print('[{}] disconnect'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) def on_response(self, msg): print('[{}] response : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , msg)) class SocketIOClient: def __init__(self, host, path): self.host = host self.path = path self.sio = socketio.Client() def connect(self): self.sio.register_namespace(MyCustomNamespace(self.path)) # 名前空間を設定 self.sio.connect(self.host) # サーバーに接続 self.sio.start_background_task(self.my_background_task, 123) # バックグラウンドタスクの登録 (123は引数の書き方の参考のため、処理には使っていない) self.sio.wait() # イベントが待ち def my_background_task(self, my_argument): # ここにバックグランド処理のコードを書く while True: input_data = input("send data:") # ターミナルから入力された文字を取得 self.sio.emit('broadcast_message', input_data, namespace = self.path) # ターミナルで入力された文字をサーバーに送信 self.sio.sleep(1) if __name__ == '__main__': sio_client = SocketIOClient('http://192.168.0.10:3000', '/test') # SocketIOClientクラスをインスタンス化 sio_client.connect()
これでSocketIOのクライアントは完成です。サーバーを起動後、このクライアントもpythonで実行してください。ターミナルに"send data:"という文字が表示されるはずです。何か文字や数字を入力して、Enterキーを押してください。サーバー側に入力した文字が送られ、同じ文字が返ってきます。これで、クライアント側の説明は終了です。