I am working on Rabbitmq and Server sent events for this I use tornado-eventsource (https://github.com/guilhermef/tornado-eventsource/tree/master/tornado_eventsource) from this code I tried to make my connection of EventSource. When my server receives messages it suooise to send it to client but it gives this error message and messages are not watched by server to send
But I guess due to a change in versions as I use python3 I get an error.
File "C:\Users\Admin\AppData\Local\Programs\Python\Python37\lib\site-packages\tornado_eventsource\handler.py", line 71, in write_message
if isinstance(msg, str) or isinstance(msg, unicode):
NameError: name 'unicode' is not defined
Please guide me regards this.
Server side code here:
import _thread
import asyncio
import time
import os
import tornado.ioloop
import tornado.web
from DateTime import DateTime
import pika
from tornado.web import Application, RequestHandler
from threading import Thread
import logging
import six
#from tornadose.handlers
import tornado_eventsource.handler
from tornado_eventsource.handler import EventSourceHandler
logging.basicConfig(level=logging.INFO)
# web socket clients connected.
clients = []
connection = pika.BlockingConnection()
logging.info('Connected:localhost')
channel = connection.channel()
print("Connection Time: ", time.time()*1000)
print("Connection Time: ", DateTime())
def get_connection():
credentials = pika.PlainCredentials('guest', 'guest')
conn = pika.BlockingConnection(pika.ConnectionParameters(host="localhost", port=5672,
virtual_host="/",
credentials=credentials))
return conn
def callback(ch, method, properties, body):
print(" [x] Received %r", fetch_DateTime(), ':', body)
time.sleep(body.count(b'.'))
#print(fetch_DateTime())
for itm in clients:
itm.write_message(body)
def start_consumers():
asyncio.set_event_loop(asyncio.new_event_loop())
channel = get_connection().channel()
channel.queue_declare(queue="my_queue")
channel.basic_consume(
queue="my_queue",
on_message_callback=callback,
auto_ack=True)
channel.start_consuming()
def fetch_DateTime():
time_milliseconds = time.time() * 1000
return(time_milliseconds)
def disconnect_to_rabbitmq():
channel.stop_consuming()
connection.close()
logging.info('Disconnected from Rabbitmq')
class EventHandler(tornado_eventsource.handler.EventSourceHandler):
def open(self):
logging.info('Server-Sent Events opened')
clients.append(self)
def on_close(self):
logging.info('Server-Sent Events closed')
clients.remove(self)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("SSE_client.html")
def make_app():
return tornado.web.Application([
(r'/', EventHandler,),
(r"/", MainHandler),
])
class WebServer(tornado.web.Application):
def __init__(self):
handlers = [ (r'/', EventHandler,),
(r"/", MainHandler), ]
settings = {'debug': True}
super().__init__(handlers, **settings)
def run(self, port=8888):
self.listen(port)
tornado.ioloop.IOLoop.instance().start()
class TestHandler(tornado.web.RequestHandler):
def get(self):
self.write("test success")
ws = WebServer()
def start_server():
asyncio.set_event_loop(asyncio.new_event_loop())
ws.run()
if __name__ == "__main__":
logging.info('Starting thread Tornado')
threadC = Thread(target=start_consumers)
threadC.start()
from threading import Thread
t = Thread(target=start_s
erver, args=())
t.daemon = True
t.start()
t.join()
try:
input("Server ready. Press enter to stop\n")
except SyntaxError:
pass
try:
logging.info('Disconnecting from RabbitMQ..')
disconnect_to_rabbitmq()
except Exception:
pass
stopTornado();
logging.info('See you...')
Support to Python 3.6+ was added on the version 2.0.0rc2
Related
I'm setting up a tornado web socket and want to use it to send log data over my network. Seems to work fine, but:
(1) Can I send data from client to server arbitrarily once the connection is established, or do I have to establish a new connection and use the on_open method to print/work with the sent message every time?
(2) In my client specifically (link): why isn't ws.close() called? Doesn't seem to happen. How can I terminate a connection then?
(3) Is there a better way to identify clients other that connection.request.remote_ip?
Code: Server
import tornado.ioloop
import tornado.web
import tornado.websocket
host = "localhost:2000"
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("<h3>HELLO</h3>")
class EchoWebSocket(tornado.websocket.WebSocketHandler):
connections = set()
ips = set()
def open(self):
self.connections.add(self)
self.ips.add(self.request.remote_ip)
print("[MASTER]: WebSocket opened by {}".format(self.request.remote_ip))
def on_message(self, message):
print("[CLIENT {}]: {}".format(self.request.remote_ip, message))
[con.write_message("[MASTER]: {}".format(message)) for con in self.connections]
def on_close(self):
self.connections.remove(self)
self.ips.remove(self.request.remote_ip)
print("[MASTER]: WebSocket closed - Client {}".format(self.request.remote_ip))
def setup():
return tornado.web.Application([(r"/", MainHandler),
(r"/ws", EchoWebSocket)])
if __name__ == "__main__":
s = tornado.httpserver.HTTPServer(setup())
s.listen(2000)
print("------------- INFO -------------\nStarted Server at {}\nSocket available at /ws".format(host))
tornado.ioloop.IOLoop.current().start()
Client:
from tornado.websocket import websocket_connect
import tornado
import time
url = "ws://localhost:2000/ws"
class Client(object):
def __init__(self, url, log=None):
self.url = url
self.ioloop = tornado.ioloop.IOLoop.current()
self.conn = None
self.log = log
def start(self):
websocket_connect(
self.url,
self.ioloop,
callback=self.on_connected,
on_message_callback=self.on_message)
self.ioloop.start()
def on_connected(self, f):
try:
self.conn = f.result()
self.conn.write_message("Client #1 connected")
except Exception as e:
print("[ERROR]: {}".format(e))
self.conn.write_message("ERROR: {}".format(e))
self.ioloop.stop()
def on_message(self, message):
if message is None:
print("[ERROR]: No message received")
self.conn.write_message("[ERROR]: No message received")
self.ioloop.stop()
else:
print(message)
def close():
self.conn.close()
if __name__ == '__main__':
ws = Client(url)
ws.start()
ws.close()
i achieved to have two threads working in my python application.
Now i want to add a callback function to the on_message to handle the receiving message in another thread.
here is my code:
import tornado.websocket
import tornado.web
import message.MessageHandler
from message.messageConstructor import MessageConstructor
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
self.send(message.MessageHandler.serialize(MessageConstructor('Engine1', '100')))
self.send("CONNECT!")
def on_message(self, rawMessage):
print rawMessage
obj = message.MessageHandler.deserialize(rawMessage)
callback(message)
def on_close(self):
print 'connection closed'
def check_origin(self, origin):
return True
def send(self, message):
self.write_message(message)
and the creation:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from websocket import connectionHandler
class WebSocketConnection():
def __init__(self, port, callback):
self.socketHandler = connectionHandler.WebSocketHandler
application = tornado.web.Application([
(r'/', self.socketHandler),
])
callback("World")
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(7004)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
tornado.ioloop.IOLoop.instance().start()
def send(self, message):
self.socketHandler.send(message)
how can i provide a callback to the not instantiated instance of the WebsocketHandler?
You can use RequestHandler.initialize to pass configuration through the Application to the handlers:
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def initialize(self, callback):
self.callback = callback
app = Application([('/', WebSocketHandler, dict(callback=callback))])
I try to create a tornado websocket, which is in the background.
to handle events of input from a another device.
Usecase: Control the raspberry pi with a websocket connection from android app.
i am able to create a connection with the following implemtation:
main.py:
import sys
from sys import stdin
from websocket import connection
def main(args):
print "start application"
port = 7004
if args[0] != port:
port = args[0]
connection.WebSocketConnection(port)
core()
def core():
run = True
while run:
userinput = stdin.readline()
print userinput
if userinput == 'quit':
print "quit app"
sys.exit()
if __name__ == "__main__":
main(sys.argv)
connection handler:
import tornado.web
import message.MessageHandler
from message.messageConstructor import MessageConstructor
from tornado.web import asynchronous
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
self.send(message.MessageHandler.serialize(MessageConstructor('Engine1', '100')))
def on_message(self, rawMessage):
print rawMessage
obj = message.MessageHandler.deserialize(rawMessage)
print(obj.target)
def on_close(self):
print 'connection closed'
def check_origin(self, origin):
return True
def send(self, message):
self.write_message(message)
connection:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from websocket import connectionHandler
class WebSocketConnection():
def __init__(self, port):
self.socketHandler = connectionHandler.WebSocketHandler
application = tornado.web.Application([
(r'/', self.socketHandler),
])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(7004)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
tornado.ioloop.IOLoop.instance().start()
def send(self, message):
self.socketHandler.send(message)
in the main.py file is function after the websocket creation and this function is never called. I guess it's a problem with the async chain of tornado.
Any suggestions?
Thanks in advance.
i figured out a way to do that, but i am not sure if this is the right way. Anyway in this set up it works:
main.py:
import sys
from sys import stdin
from websocket import connection
from tornado.ioloop import IOLoop
def main(args):
print "start application"
port = 7004
if args[0] != port:
port = args[0]
connection.WebSocketConnection(port)
core()
IOLoop.instance().start()
def core():
run = True
while run:
userinput = stdin.readline()
print userinput
if userinput == 'quit':
print "quit app"
sys.exit()
if __name__ == "__main__":
main(sys.argv)
connection.py:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
from websocket import connectionHandler
class WebSocketConnection():
def __init__(self, port):
self.socketHandler = connectionHandler.WebSocketHandler
application = tornado.web.Application([
(r'/', self.socketHandler),
])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(7004)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
def send(self, message):
self.socketHandler.send(message)
as you can see i start the loop just in the main file after calling the core function. In this way it works.
but as i said before i am not sure if this i really correct.
Is it possible, if multiple socket clients are connected to a tornado websocket server, to send a message to a specific one?
I don't know if there is a possibility of getting a client's id and then send a message to that id.
My client code is:
from tornado.ioloop import IOLoop, PeriodicCallback
from tornado import gen
from tornado.websocket import websocket_connect
class Client(object):
def __init__(self, url, timeout):
self.url = url
self.timeout = timeout
self.ioloop = IOLoop.instance()
self.ws = None
self.connect()
PeriodicCallback(self.keep_alive, 20000, io_loop=self.ioloop).start()
self.ioloop.start()
#gen.coroutine
def connect(self):
print "trying to connect"
try:
self.ws = yield websocket_connect(self.url)
except Exception, e:
print "connection error"
else:
print "connected"
self.run()
#gen.coroutine
def run(self):
while True:
msg = yield self.ws.read_message()
print msg
if msg is None:
print "connection closed"
self.ws = None
break
def keep_alive(self):
if self.ws is None:
self.connect()
else:
self.ws.write_message("keep alive")
if __name__ == "__main__":
client = Client("ws://xxx", 5 )
When your client connects to the websocket server will be invoked method 'open' on WebSocketHandler, in this method you can keep socket in the Appliaction.
Like this:
from tornado import web
from tornado.web import url
from tornado.websocket import WebSocketHandler
class Application(web.Application):
def __init__(self):
handlers = [
url(r"/websocket/server/(?P<some_id>[0-9]+)/", WebSocketServer),
]
web.Application.__init__(self, handlers)
self.sockets = {}
class WebSocketServer(WebSocketHandler):
def open(self, some_id):
self.application.sockets[some_id] = self
def on_message(self, message):
self.write_message(u"You said: " + message)
def on_close(self):
print("WebSocket closed")
You also may use message for connection, in this message you have to tell socket id and save socket into the Application.
I'm trying to set up a small example of a public Twitter stream over websockets. This is my websocket.py, and it's working.
What I'm wondering is: how can I interact with the websocket from 'outside' the class WSHandler (ie. not only answer when receiving a message from websocket.js)? Say I want to run some other function within this same script that would post "hello!" every five seconds and send that to the websocket (browser) without any interaction from client-side. How could I do that?
So it's kind of a fundamental beginner's question, I suppose, about how to deal with classes as those below. Any pointers in any direction would be greatly appreciated!
import os.path
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
# websocket
class FaviconHandler(tornado.web.RequestHandler):
def get(self):
self.redirect('/static/favicon.ico')
class WebHandler(tornado.web.RequestHandler):
def get(self):
self.render("websockets.html")
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
self.write_message("Hi, client: connection is made ...")
def on_message(self, message):
print 'message received: \"%s\"' % message
self.write_message("Echo: \"" + message + "\"")
if (message == "green"):
self.write_message("green!")
def on_close(self):
print 'connection closed'
handlers = [
(r"/favicon.ico", FaviconHandler),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': 'static'}),
(r'/', WebHandler),
(r'/ws', WSHandler),
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "static"),
)
application = tornado.web.Application(handlers, **settings)
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
You could call a
IOLoop.add_timeout(deadline, callback)
that calls the callback at specified deadline timeout (one shot, but you can reschedule), or use the
tornado.ioloop.PeriodicCallback if you have a more periodic task.
See: http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_timeout
Update: some example
import datetime
def test():
print "scheduled event fired"
...
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
main_loop = tornado.ioloop.IOLoop.instance()
# Schedule event (5 seconds from now)
main_loop.add_timeout(datetime.timedelta(seconds=5), test)
# Start main loop
main_loop.start()
it calls test() after 5 seconds.
Update 2:
import os.path
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
# websocket
class FaviconHandler(tornado.web.RequestHandler):
def get(self):
self.redirect('/static/favicon.ico')
class WebHandler(tornado.web.RequestHandler):
def get(self):
self.render("websockets.html")
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
self.write_message("Hi, client: connection is made ...")
tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=5), self.test)
def on_message(self, message):
print 'message received: \"%s\"' % message
self.write_message("Echo: \"" + message + "\"")
if (message == "green"):
self.write_message("green!")
def on_close(self):
print 'connection closed'
def test(self):
self.write_message("scheduled!")
handlers = [
(r"/favicon.ico", FaviconHandler),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': 'static'}),
(r'/', WebHandler),
(r'/ws', WSHandler),
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "static"),
)
application = tornado.web.Application(handlers, **settings)
import datetime
if __name__ == "__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
I stumbled upon similar problem. Here is my solution. Hope this will be helpful to someone out there
wss = []
class wsHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'Online'
if self not in wss:
wss.append(self)
def on_close(self):
print 'Offline'
if self in wss:
wss.remove(self)
def wsSend(message):
for ws in wss:
ws.write_message(message)
To send message to your websockets simply use this:
wsSend(message)
wsSend update
I've been getting exceptions with wsSend once in a while. In order to fix it I've modified code a bit to following:
def wsSend(message):
for ws in wss:
if not ws.ws_connection.stream.socket:
print "Web socket does not exist anymore!!!"
wss.remove(ws)
else:
ws.write_message(message)
One way to also do this is to use a pub-sub module.
Meaning you have your connections subscribe to it, and rather than setting timeouts for every single connection, you just set one timeout to publish after said period of time.
Probably one of the most implemented is redis. There are also some modules specifically for tornado: toredis or brükva for example.
Of course this might not be necessary for just a simple page, but scales really well and is also very nice to maintain/extend once you've set it up.