Programming Tips blog

テキトーにTIPSを紹介します

ラズパイ + Python + tornadoでWebSocketサーバ

ハードウェア

ソフトウェア

 ソフトウェアについては下記を想定しています。

tornadoインストール

 まず、tornadoをインストールします。

pip3 install tornado

Pythonプログラム

インポート

 プログラムの、tornado関連のインポートは下記の通りです。

import asyncio
import tornado.ioloop
import tornado.web
import tornado.websocket

WebSocketHandlerクラスをオーバーライド

 WebSocketHandlerクラスをオーバーライドします。

class BrowserWebSocketHandler(tornado.websocket.WebSocketHandler):
	clients = []	# WebSocketクライアントハンドラリスト
	
	# WebSocketコネクションがopenしたときに実行
	# 備考:メソッド名は変えられない。
	def open(self):
		print("Session Opened. IP Address:" + self.request.remote_ip)
		# オープンしたWebSocketハンドラがリストにない場合
		if self not in BrowserWebSocketHandler.clients:
			# WebSocketハンドラリストに追加する
			BrowserWebSocketHandler.clients.append(self)
		return
	
	
	# WebSocketクライアントからメッセージを受信したときに実行
	# 備考:メソッド名は変えられない。
	def on_message(self, message):
		print(message)
		return
	
	
	# WebSocketコネクションが閉じられたときに実行
	# 備考:メソッド名は変えられない。
	def on_close(self):
		print("CLOSE")
		# 自オブジェクトがWebSocketクライアントリストにあった場合
		if self in BrowserWebSocketHandler.clients:
			# WebSocketをクローズする
			self.close()
			# WebSocketクライアントリストからWebSocketハンドラを削除する
			BrowserWebSocketHandler.clients.remove(self)
		return

RequestHandlerをオーバーライド

 RequestHandlerをオーバーライドします。

class BrowserRequestHandler(tornado.web.RequestHandler):
	# HTMLファイルを読み込むときに実行
	# 備考:メソッド名は変えられない。
	# 備考:HTTPのGETメソッドのこと??
	def get(self):
		# HTMLへの、OS上の絶対パス。つまり、URLの絶対パスではない。
		self.render("/home/user/public_html/hogehoge/index.html")
		return

WebSocketクライアントに対してメッセージ送信

 マルチスレッドで「BrowserThread」を動かしています。
 その中でtornadoをループ処理しています。
 「receive_message_box」はWebSocketクライアントに送るメッセージを格納したリストです。

# WebSocketクライアントに対してメッセージ送信(ループ内容)
def BrowserSendMessage():
	global kill_flag
	
	# プログラム終了の場合
	if kill_flag:
		# tornadoのインスタンスを停止する
		tornado.ioloop.IOLoop.instance().stop()
		return
	
	# tornado IOLoopハンドラ取得
	ioloop = tornado.ioloop.IOLoop.current()
	# 1マイクロ秒ごとに自関数を呼び出す(再帰、なんちゃってループ処理)
	ioloop.add_timeout(datetime.timedelta(microseconds=1), BrowserSendMessage)
	
	# WebSocketクライアントに送信するメッセージがなく、かつ、WebSocketクライアントもない場合
	if (len(receive_message_box) < 1) and (len(BrowserWebSocketHandler.clients) < 1):
		return
	
	# receive_message_boxの中身を全部送信する
	while len(receive_message_box) > 0:
		# 全てのブラウザに対してメッセージを送信する
		for c in BrowserWebSocketHandler.clients:
			try:
				# ブラウザにメッセージを送信する
				c.write_message(receive_message_box[0])
			except:
				# WebSocketをクローズする
				c.close()
				# WebSocketハンドラリストから削除する
				BrowserWebSocketHandler.clients.remove(c)
		# メッセージを削除する
		del receive_message_box[0]
	
	return


# ブラウザスレッド
def BrowserThread():
	# イベントループ設定
	asyncio.set_event_loop(asyncio.new_event_loop())
	
	# tornadoアプリケーションハンドラ作成
	# パス表記はURL絶対パスになる。つまり、OS上のパスではない。
	application = tornado.web.Application([(r"/hogehoge/index.html", BrowserRequestHandler),(r"/hogehoge/websocket", BrowserWebSocketHandler), ])
	
	# WebSocket listen
	application.listen(8888)
	
	# ループ内容設定
	BrowserSendMessage()
	
	# tornadoスタート
	tornado.ioloop.IOLoop.instance().start()	# ここでブロッキングとなる
	
	print("BrowserThread:終了しました。")
	return

 ランキングに参加しています。下記バナーをクリックしていただけるとありがたいです。