やっとできた。ポイントは
(1)通常のserver.py+index.html構成のうち、index.htmlを無視したものが欲しいサーバプログラム。index.htmlのscript領域に記載したプログラムの挙動が、欲しいclientプログラム。
clientプログラムの実装方式は任意。ws4pyライブラリ+tornadoとか、
lirisのwebsocket-clientライブラリとか。
(2)Clientの接続時にHandshake status 200エラーが出る→ルート(ベースルータ)ではなくWebsocketトランスポートにアクセスする。Websocketトランスポートのアドレスはサーバ側で指定する
→tornadoだとapplication = tornado.web.Applicationの設定で(r'/ws', WSHandler),の様に/wsに割り当てる。
(参考:https://github.com/mrjoes/tornadio/issues/21)
1. サーバ側: tornado使用
http://www.remwebdevelopment.com/blog/python/simple-websocket-server-in-python-144.html
server.py
import tornado.ioloop import tornado.web import tornado.websocket import tornado.template class MainHandler(tornado.web.RequestHandler): def get(self): loader = tornado.template.Loader(".") self.write(loader.load("index.html").generate()) class WSHandler(tornado.websocket.WebSocketHandler): def open(self): print 'connection opened...' self.write_message("The server says: 'Hello'. Connection was accepted.") def on_message(self, message): self.write_message("The server says: " + message + " back at you") print 'received:', message def on_close(self): print 'connection closed...' application = tornado.web.Application([ (r'/ws', WSHandler), (r'/', MainHandler), (r"/(.*)", tornado.web.StaticFileHandler, {"path": "./resources"}), ]) if __name__ == "__main__": application.listen(9090) tornado.ioloop.IOLoop.instance().start()
index.htmlはコピペ(or script領域を抜く→あとで好きな挙動に入れ替える)
index.html(今は使わないけど置いとく)
<!DOCTYPE html> <html> <head> <title>WebSockets Client</title> <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script> </head> <body> Enter text to send to the websocket server: <div id="send"> <input type="text" id="data" size="100"/><br> <input type="button" id="sendtext" value="send text"/> </div> <div id="output"></div> </body> </html> <script> jQuery(function($){ if (!("WebSocket" in window)) { alert("Your browser does not support web sockets"); }else{ setup(); } function setup(){ // Note: You have to change the host var // if your client runs on a different machine than the websocket server var host = "ws://localhost:9090/ws"; var socket = new WebSocket(host); //console.log("socket status: " + socket.readyState); var $txt = $("#data"); var $btnSend = $("#sendtext"); $txt.focus(); // event handlers for UI $btnSend.on('click',function(){ var text = $txt.val(); if(text == ""){ return; } socket.send(text); $txt.val(""); }); $txt.keypress(function(evt){ if(evt.which == 13){ $btnSend.click(); } }); // event handlers for websocket if(socket){ socket.onopen = function(){ //alert("connection opened...."); } socket.onmessage = function(msg){ showServerResponse(msg.data); } socket.onclose = function(){ //alert("connection closed...."); showServerResponse("The connection has been closed."); } }else{ console.log("invalid socket"); } function showServerResponse(txt){ var p = document.createElement('p'); p.innerHTML = txt; document.getElementById('output').appendChild(p); } } }); </script>
2-a. クライアント側その1→websocket-clientライブラリ
client.py
https://github.com/liris/websocket-client
import websocket import thread import time def on_message(ws, message): print message def on_error(ws, error): print error def on_close(ws): print "### closed ###" def on_open(ws): def run(*args): for i in range(5): time.sleep(1) ws.send("Hello %d" % i) time.sleep(1) ws.close() print "thread terminating..." thread.start_new_thread(run, ()) if __name__ == "__main__": websocket.enableTrace(True) ws = websocket.WebSocketApp("ws://localhost:9090/ws", on_message = on_message, on_error = on_error, on_close = on_close) ws.on_open = on_open ws.run_forever()
2-b. クライアント側その2→ws4py+tornado
こっちの方がOctoPrintとかと連動させるには良さそう
https://ws4py.readthedocs.org/en/latest/sources/clienttutorial/#tornado
client.py
from ws4py.client.tornadoclient import TornadoWebSocketClient from tornado import ioloop class MyClient(TornadoWebSocketClient): def opened(self): for i in range(0, 200, 25): self.send("*" * i) def received_message(self, m): print m if len(m) == 175: self.close(reason='Bye bye') def closed(self, code, reason=None): ioloop.IOLoop.instance().stop() ws = MyClient('ws://localhost:9090/ws', protocols=['http-only', 'chat']) ws.connect() ioloop.IOLoop.instance().start()
実行
$ python server.py $ python client.py