2019/08/17

PythonでSocketIO(サーバー編)

背景


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

記事の目的


PythonでSocketIOサーバーを作成する

python-socketio


python-socketioを使用したSocketIOサーバーを作成する

選定理由

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

導入方法

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

テンプレート

python-socketioの使用テンプレートは、下記の通りである。
  1. # socketioライブラリのインポート
  2. import socketio
  3. # ログ出力用ライブラリのインポート
  4. from logging import getLogger, StreamHandler, DEBUG, INFO, ERROR
  5. # 時間関係ライブラリのインポート
  6. import time
  7. # 終了シグナルをキャッチするライブラリのインポート
  8. import signal
  9. # eventletライブラリのインポート
  10. import eventlet
  11. # マルチスレッドライブラリをインポート
  12. import threading
  13. # ロガーのインスタンス作成
  14. logger = getLogger(__name__)
  15. stream_handler = StreamHandler()
  16. # ログレベルを設定
  17. log_level = INFO
  18. logger.setLevel(log_level)
  19. stream_handler.setLevel(log_level)
  20. # ロガーにハンドラーをセット
  21. logger.addHandler(stream_handler)
  22. # SocketIOのテンプレート
  23. class SocketIOServer:
  24. # namespaceの設定用クラス
  25. class NamespaceClass(socketio.Namespace):
  26. def on_connect(self, sid, environ):
  27. pass
  28. def on_disconnect(self, sid):
  29. pass
  30. def on_message(self, sid, data):
  31. pass
  32. def on_client_to_server(self, sid, data):
  33. pass
  34. # 接続時に呼ばれるイベント
  35. def on_connect(self, sid, environ):
  36. logger.info('Connected to client (sid: %s, environ: %s)',\
  37. str(sid), str(environ))
  38. self.is_connect_ = True
  39. # 切断時に呼ばれるイベント
  40. def on_disconnect(self, sid):
  41. logger.info('Disconnected from client (sid: %s)',str(sid))
  42. self.is_connect_ = False
  43. # サーバーからデータ送信(send)する
  44. def send_data(self, data):
  45. try:
  46. self.sio_.send(data, namespace=self.namespace_)
  47. except:
  48. logger.error('Has not connected to client')
  49. else:
  50. logger.info('Send message %s (namespace="%s")',\
  51. str(data), self.namespace_)
  52. # 独自定義のイベント名「server_to_client」で、サーバーからデータ送信(emit)する
  53. def emit_data(self, data):
  54. try:
  55. self.sio_.emit('server_to_client', data, namespace=self.namespace_)
  56. except:
  57. logger.error('Has not connected to client')
  58. else:
  59. logger.info('Emit message %s (namespace="%s")',\
  60. str(data), self.namespace_)
  61. def on_message(self, sid, data):
  62. logger.info('Received message %s', str(data))
  63. self.send_data({'test_ack': 'send_from_server'})
  64. # クライアントからイベント名「on_client_to_server」でデータがemitされた時に呼ばれる
  65. def on_client_to_server(self, sid, data):
  66. logger.info('Received message %s', str(data))
  67. self.emit_data({'test_ack': 'emit_from_server'})
  68. # Namespaceクラス内の各イベントをオーバーライド
  69. def overload_event(self):
  70. self.Namespace.on_connect = self.on_connect
  71. self.Namespace.on_disconnect = self.on_disconnect
  72. self.Namespace.on_message = self.on_message
  73. self.Namespace.on_client_to_server = self.on_client_to_server
  74. # 初期化
  75. def __init__(self,ip,port,namespace):
  76. self.ip_ = ip
  77. self.port_ = port
  78. self.namespace_ = namespace
  79. self.is_connect_ = False
  80. self.sio_ = socketio.Server(async_mode='eventlet')
  81. self.app_ = socketio.WSGIApp(self.sio_)
  82. self.Namespace = self.NamespaceClass(self.namespace_)
  83. self.overload_event()
  84. self.sio_.register_namespace(self.Namespace)
  85. # 接続確認
  86. def isConnect(self):
  87. return self.is_connect_
  88. # 切断
  89. def disconnect(self,sid):
  90. try:
  91. self.sio_.disconnect(sid,namespace=self.namespace_)
  92. except:
  93. logger.error('Cannot disconnect from Client(sid="%s", namespace="%s")',\
  94. namespace=self.namespace_)
  95. else:
  96. self.is_connect_ = False
  97. # 開始
  98. def start(self):
  99. try:
  100. logger.info('Start listening(%s:%d, namespace="%s")',\
  101. self.ip_,self.port_,self.namespace_)
  102. eventlet.wsgi.server(eventlet.listen((self.ip_, self.port_)), self.app_)
  103. except:
  104. logger.error('Cannot start listening(%s:%d, namespace="%s")',\
  105. self.ip_,self.port_,self.namespace_)
  106. # メインの処理
  107. def run(self):
  108. p = threading.Thread(target=self.start)
  109. p.setDaemon(True)
  110. p.start()
  111. if __name__ == '__main__':
  112. # Ctrl + C (SIGINT) で終了
  113. signal.signal(signal.SIGINT, signal.SIG_DFL)
  114. # SocketIO Server インスタンスを生成
  115. sio_server = SocketIOServer('localhost', 10000, '/test')
  116. # SocketIO Server インスタンスを実行
  117. sio_server.run()
  118. # 接続待ち
  119. while not sio_server.isConnect():
  120. time.sleep(1)
  121. # 切断待ち
  122. while sio_server.isConnect():
  123. time.sleep(1)
  124. logger.info('Finish')
  125. # 終了
  126. exit()

備考

  • namespaceは、必ず「/」(スラッシュ)から始まる必要がある
  • eventletを使用した場合、他の作業スレッドから、SocketIO内のEmitやSendを呼び出せない
  • 上記テンプレートは、クライアントと通信テストができる

まとめ


  • Python-SocketIOを利用したサーバーモジュールを作成した

参考文献



変更履歴


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

0 件のコメント:

コメントを投稿

MQTTの導入

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