2019/08/17

PythonでSocketIO(クライアント編)

背景


PythonでSocketIO通信を行う必要があったため、今後の開発の効率化のためにSocketIO通信を行うクラスのテンプレートを作成する。

記事の目的


PythonでSocketIOクライアントを作成する

python-socketio


python-socketioを使用したSocketIOクライアントを作成する

選定理由

python-socketioの選定理由は、下記の通りである。

導入方法

python-socketioの導入手順は、下記の通りである。
  1. pipを利用して必要なライブラリをインストールする
  2. $ pip install requests python-socketio

テンプレート

python-socketioの使用テンプレートは、下記の通りである。
# socketioライブラリのインポート
import socketio
# ログ出力用ライブラリのインポート
from logging import getLogger, StreamHandler, DEBUG, INFO, ERROR
# 時間関係ライブラリのインポート
import time
# 終了シグナルをキャッチするライブラリのインポート
import signal
# マルチスレッドライブラリのインポート
import threading

# ロガーのインスタンス作成
logger         = getLogger(__name__)
stream_handler = StreamHandler()

# ログレベルを設定
log_level = INFO
logger.setLevel(log_level)
stream_handler.setLevel(log_level)

# ロガーにハンドラーをセット
logger.addHandler(stream_handler)

# SocketIOのテンプレート
class SocketIOClient:
    # namespaceの設定用クラス
    class NamespaceClass(socketio.ClientNamespace):
        def on_connect(self):
            pass
        def on_disconnect(self):
            pass
        def on_message(self, data):
            pass
        def on_server_to_client(self, data):
            pass
    # 接続時に呼ばれるイベント
    def on_connect(self):
        logger.info('Connected to server (%s:%d, namespace="%s", query="%s")',\
                     self.ip_,self.port_,self.namespace_,self.query_)
        self.is_connect_ = True
    # 切断時に呼ばれるイベント
    def on_disconnect(self):
        logger.info('Disconnected from server (%s:%d, namespace="%s", query="%s")',\
                     self.ip_,self.port_,self.namespace_,self.query_)
        self.is_connect_ = False
    def on_message(self, data):
        logger.info('Received message %s', str(data)) 
    # サーバーからイベント名「server_to_client」でデータがemitされた時に呼ばれる
    def on_server_to_client(self, data):
        logger.info('Received message %s', str(data)) 
    # Namespaceクラス内の各イベントをオーバーライド
    def overload_event(self):
        self.Namespace.on_connect          = self.on_connect
        self.Namespace.on_disconnect       = self.on_disconnect
        self.Namespace.on_message          = self.on_message
        self.Namespace.on_server_to_client = self.on_server_to_client
    # 初期化
    def __init__(self,ip,port,namespace,query):
        self.ip_         = ip
        self.port_       = port
        self.namespace_  = namespace
        self.query_      = query
        self.is_connect_ = False
        self.sio_        = socketio.Client()
        self.Namespace   = self.NamespaceClass(self.namespace_)
        self.overload_event()
        self.sio_.register_namespace(self.Namespace)
    # 接続確認
    def isConnect(self):
        return self.is_connect_
    # 接続
    def connect(self):
        # 接続先のURLとqueryの設定
        url = 'ws://'+self.ip_+':'+str(self.port_)+'?query='+self.query_
        logger.info('Try to connect to server(%s:%d, namespace="%s", query="%s")',\
                     self.ip_,self.port_,self.namespace_,self.query_)
        try:
            self.sio_.connect(url, namespaces=self.namespace_)            
        except: 
            logger.error('Cannot connect to server(%s:%d, namespace="%s", query="%s")',\
                          self.ip_,self.port_,self.namespace_,self.query_)
        else: 
            if not self.is_connect_:
                logger.error('Namespace may be invalid.(namespace="%s")',\
                              self.namespace_)   
    # 切断
    def disconnect(self):
        try:
            self.sio_.disconnect()
        except:
            logger.error('Cannot disconnect from server(%s:%d, namespace="%s", query="%s")',\
                          self.ip_,self.port_,self.namespace_,self.query_)
        else:
            self.is_connect_ = False
    # 再接続
    def reconnect(self):
        self.disconnect()
        time.sleep(5)
        self.connect()
    # クライアントからデータ送信(send)する
    def sendData(self, data):
        try:
            self.sio_.send(data, namespace=self.namespace_)
        except:
            logger.error('Has not connected to server(%s:%d, namespace="%s", query="%s")',\
                          self.ip_,self.port_,self.namespace_,self.query_)
        else:
            logger.info('Send message %s (namespace="%s")', str(data), self.namespace_) 
    # 独自定義のイベント名「client_to_server」で、クライアントからデータ送信(emit)する
    def emitData(self, data):
        try:
            self.sio_.emit('client_to_server', data, namespace=self.namespace_)
        except:
            logger.error('Has not connected to server(%s:%d, namespace="%s", query="%s")',\
                          self.ip_,self.port_,self.namespace_,self.query_)
        else:
            logger.info('Emit message %s (namespace="%s")', \
                         str(data), self.namespace_)  
    # メインの処理
    def run(self):
        while True:
            self.connect()
            time.sleep(1)
            if self.is_connect_:
                break
        p = threading.Thread(target=self.sio_.wait)
        p.setDaemon(True)
        p.start()
    
if __name__ == '__main__':
    # Ctrl + C (SIGINT) で終了
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    # SocketIO Client インスタンスを生成
    sio_client = SocketIOClient('localhost', 10000, '/test', 'secret')
    # SocketIO Client インスタンスを実行
    sio_client.run()
    # データを送信
    for i in range(10):
        sio_client.sendData({'test_data': 'send_from_client'})
        sio_client.emitData({'test_data': 'emit_from_client'})
        time.sleep(1)
    # 切断
    sio_client.disconnect()
    logger.info('Finish')
    # 終了
    exit()

備考

  • namespaceは、必ず「/」(スラッシュ)から始まる必要がある
  • 上記テンプレートは、サーバーと通信テストができる

まとめ


  • Python-SocketIOを利用したクライアントモジュールを作成した

参考文献



変更履歴


  1. 2019/08/17: 新規作成

0 件のコメント:

コメントを投稿

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...