Python & Raspberry Pi 開発ブログ ☆彡

PythonとRaspberry Piの基本的な使い方をわかりやすく解説。初心者、入門者必見!!

Python SocketIO通信の仕方:サーバー編

今回は、PythonでSocket.IOモジュールを使って、クライアントとサーバー間でコミュニケーションする方法を紹介したいと思います。webページの値を更新したり、webページから入力された文字等を取得できたりしますので、とても重宝します。それでは、サーバー側のプログラムについて説明してきます。

Socket.IOモジュールのインストール

下記コマンドでSocket.IOモジュールをインストールします。eventletというwsgiサーバーも使いますので、併せてインストールしておきましょう。

$ pip install python-socketio
$ pip install eventlet

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のパターンを下記の3つ作っています。

・on_sid_message ⇒ 送信してきたクライアントのみにメッセージを送る関数
・on_skip_sid_message ⇒ 送信してきたクライアントを除く全ての接続しているクライアントにメッセージを送信する関数
・on_broadcast_message ⇒ 接続しているすべてのクライアントにメッセージを送る関数

import eventlet
import socketio
from datetime import datetime

class MyCustomNamespace(socketio.Namespace): # 名前空間を設定するクラス

    def on_connect(self, sid, environ):
        print('[{}] connet sid : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , sid))
        print('[{}] connet env : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , environ))
            
    def on_sid_message(self, sid, msg): # 送信してきたクライアントだけにメッセージを送る関数
        self.emit('response', msg, room=sid)
        print('[{}] emit sid : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , msg))

    def on_skip_sid_message(self, sid, msg):# 送信してきたクライアントを除く全ての接続しているクライアントにメッセージを送信する関数
        self.emit('response', msg, skip_sid=sid) 
        print('[{}] emit skip sid : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , msg))

    def on_broadcast_message(self, sid, msg):# 接続しているすべてのクライアントにメッセージを送る関数
        self.emit('response', msg)
        print('[{}] emit all : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S') , msg))
    
    def on_disconnect(self, sid):
        print('[{}] disconnect'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    
if __name__ == '__main__':
    
    sio = socketio.Server(cors_allowed_origins='*') # CORSのエラーを無視する設定
    sio.register_namespace(MyCustomNamespace('/test')) # 名前空間を設定
    app = socketio.WSGIApp(sio) # wsgiサーバーミドルウェア生成
    eventlet.wsgi.server(eventlet.listen(('192.168.0.10',3000)), app) # wsgiサーバー起動
    

これでSocketIOのサーバーは完成です。このサーバーだけ起動しても何も起きません。クライアントから接続され、メッセージのやりとりをしてはじめて動きがわかります。クライアントのコードは下記ページを見てください。

www.raspberrypirulo.net

ラズパイ ソフトウェアに戻る
トップページに戻る