python Tornado websockets how to send message every X seconds? - python

I am trying to cobble together a test which allows websockets clients to connect to a Tornado server and I want the Tornado server to send out a message to all clients every X seconds.
The reason I am doing this is because wbesockets connections are being silently dropped somewhere and I am wondering of periodic "pings" sent by the websockets server will maintain the connection.
I'm afraid it's a pretty noobish question and the code below is rather a mess. I just don't have my head wrapped around Tornado and scope enough to make it work.
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import tornado.gen
import time
from tornado import gen
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'http://mailapp.crowdwave.com/girlthumb.jpg'
self.write_message("http://www.example.com/girlthumb.jpg")
def on_message(self, message):
print 'Incoming message:', message
self.write_message("http://www.example.com/girlthumb.jpg")
def on_close(self):
print 'Connection was closed...'
#gen.engine
def f():
yield gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 8)
self.write_message("http://www.example.com/x.png")
print 'x'
#gen.engine
def g():
yield gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 4)
self.write_message("http://www.example.com/y.jpg")
print 'y'
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
tornado.ioloop.IOLoop.instance().add_callback(f)
tornado.ioloop.IOLoop.instance().add_callback(g)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Why don't you try write a scheduler inside it? :)
def schedule_func():
#DO SOMETHING#
#milliseconds
interval_ms = 15
main_loop = tornado.ioloop.IOLoop.instance()
sched = tornado.ioloop.PeriodicCallback(schedule_func,interval_ms, io_loop = main_loop)
#start your period timer
sched.start()
#start your loop
main_loop.start()

Found that the accepted answer for this is almost exactly what I want:
How to run functions outside websocket loop in python (tornado)
With a slight modification, the accepted answer at the above link continually sends out ping messages. Here is the mod:
Change:
def test(self):
self.write_message("scheduled!")
to:
def test(self):
self.write_message("scheduled!")
tornado.ioloop.IOLoop.instance().add_timeout(datetime.timedelta(seconds=5), self.test)

Related

How to call Python Tornado Websocket Server inside another Python

I would like to implement Python Tornado Websocket Server inside another Python (main) and trigger send messages when needed. The main creates two threads. One of them is for Python Server and the other is for my loop that will trigger message.
When I start server from initial, server works fine however because its endless following main files doesn't run. So I start server inside a thread but this time I receive "RuntimeError: There is no current event loop in thread 'Thread-1 (start_server)'"
Main.py
import tornadoserver
import time
from threading import Lock, Thread
class Signal:
def __init__(self):
#self.socket = tornadoserver.initiate_server()
print("start")
def start_server(self):
print("start Server")
self.socket = tornadoserver.initiate_server()
def brd(self):
print("start Broad")
i = 0
while True:
time.sleep(3)
self.socket.send(i)
i = i + 1
def job(self):
# --------Main--------
threads = []
for func in [self.start_server, self.brd, ]:
threads.append(Thread(target=func))
threads[-1].start()
for thread in threads:
thread.join()
Signal().job()
tornadoserver.py
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.websocket as ws
from tornado.options import define, options
import time
define('port', default=4041, help='port to listen on')
ws_clients = []
class web_socket_handler(ws.WebSocketHandler):
#classmethod
def route_urls(cls):
return [(r'/', cls, {}), ]
def simple_init(self):
self.last = time.time()
self.stop = False
def open(self):
self.simple_init()
if self not in ws_clients:
ws_clients.append(self)
print("New client connected")
self.write_message("You are connected")
def on_message(self, message):
if self in ws_clients:
print("received message {}".format(message))
self.write_message("You said {}".format(message))
self.last = time.time()
def on_close(self):
if self in ws_clients:
ws_clients.remove(self)
print("connection is closed")
self.loop.stop()
def check_origin(self, origin):
return True
def send_message(self, message):
self.write_message("You said {}".format(message))
def send(message):
for c in ws_clients:
c.write_message(message)
def initiate_server():
# create a tornado application and provide the urls
app = tornado.web.Application(web_socket_handler.route_urls())
# setup the server
server = tornado.httpserver.HTTPServer(app)
server.listen(options.port)
# start io/event loop
tornado.ioloop.IOLoop.instance().start()
Using Google I found tornado issue
Starting server in separate thread gives... RuntimeError: There is no current event loop in thread 'Thread-4' · Issue #2308 · tornadoweb/tornado
and it shows that it has to use
asyncio.set_event_loop(asyncio.new_event_loop())
to run event loop in new thread
Something like this
import asyncio
# ...
def initiate_server():
asyncio.set_event_loop(asyncio.new_event_loop()) # <---
# create a tornado application and provide the urls
app = tornado.web.Application(web_socket_handler.route_urls())
# setup the server
server = tornado.httpserver.HTTPServer(app)
server.listen(options.port)
# start io/event loop
tornado.ioloop.IOLoop.instance().start()

python - handle tornado connections into while loop

I have a server running a loop that reads data from a device and I want to send them to all clients who connect on a websocket on tornado.
I tried putting the loop inside the open function but then it can't handle on_close function or new connections.
What is best practice to do that?
#!/usr/bin/env python
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
class MyWebSocketServer(tornado.websocket.WebSocketHandler):
def open(self):
print('new connection'+self.request.remote_ip)
try:
while True:
'''
read and send data
'''
except Exception,error:
print "Error on Main: "+str(error)
def on_close(self):
print('connection closed'+self.request.remote_ip)
application=tornado.web.Application([(r'/ws',MyWebSocketServer),])
if __name__=="__main__":
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8000)
print('start')
tornado.ioloop.IOLoop.instance().start()
Thanks
Here's a full example about running your blocking code in a separate thread and broadcasting messages to all connected clients.
...
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=1) # spawn only 1 thread
class MyWebSocketServer(tornado.websocket.WebSocketHandler):
connections = set() # create a set to hold connections
def open(self):
# put the new connection in connections set
self.connections.add(self)
def on_close(self):
print('connection closed'+self.request.remote_ip)
print('new connection'+self.request.remote_ip)
# remove client from connections
self.connections.remove(self)
#classmethod
def send_message(cls, msg):
for client in cls.connections:
client.write_message(msg)
def read_from_serial(loop, msg_callback):
"""This function will read from serial
and will run in aseparate thread
`loop` is the IOLoop instance
`msg_allback` is the function that will be
called when new data is available from usb
"""
while True:
# your code ...
# ...
# when you get new data
# tell the IOLoop to schedule `msg_callback`
# to send the data to all clients
data = "new data"
loop.add_callback(msg_callback, data)
...
if __name__ == '__main__':
loop = tornado.ioloop.IOLoop.current()
msg_callback = MyWebSocketServer.send_message
# run `read_from_serial` in another thread
executor.submit(read_from_serial, loop, msg_callback)
...
loop.start()

echo http server tornado

How can I create an HTTP echo server from Tornado?
#!/usr/bin/env python
import signal
from tornado.ioloop import IOLoop
from tornado.tcpserver import TCPServer
import tornado.web
def handle_signal(sig, frame):
IOLoop.instance().add_callback(IOLoop.instance().stop)
class EchoServer(TCPServer):
def handle_stream(self, stream, address):
self._stream = stream
self._read_line()
def _read_line(self):
self._stream.read_until('\n' ,self._handle_read)
def _handle_read(self, data):
self._stream.write(data, '\n')
self._read_line()
if __name__ == '__main__':
signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal)
server = EchoServer()
server.bind(8001)
server.start(25)
IOLoop.instance().start()
IOLoop.instance().close()
How do I make of this http echo server
what's wrong? not much i am newbie
Thanks!
Your question would be more clear if you explained what happened when you run this code, and what you expected instead.
One TCPServer object may handle many connections, so instead of assigning to self.stream in handle_stream, you should make a new object to handle this stream.
The second argument to stream.write is a callback; it looks like you meant self._stream.write(data + '\n').

Tornado and Autobahn-python listening on the same port

Recently I started a small personal project. It's a realtime web system based on asyncio and autobahn-python. However I also would like to serve some static files via HTTP and do it from the same process. My HTTP server is Tornado sitting on top of asyncio event loop and everything works perfectly fine except that I have to start tornado and autobahn handlers on different ports. Here is a stripped down version of what I currently have:
import six
import datetime
import asyncio
import tornado.web
import tornado.httpserver
import tornado.netutil
from tornado.platform.asyncio import AsyncIOMainLoop
from autobahn.wamp import router
from autobahn.asyncio import wamp, websocket
# WAMP server
class MyBackendComponent(wamp.ApplicationSession):
def onConnect(self):
self.join(u"realm1")
#asyncio.coroutine
def onJoin(self, details):
def utcnow():
now = datetime.datetime.utcnow()
return six.u(now.strftime("%Y-%m-%dT%H:%M:%SZ"))
reg = yield from self.register(utcnow, 'com.timeservice.now')
# HTTP server
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world!")
tornado_app = tornado.web.Application(
[
(r"/", MainHandler),
],
)
if __name__ == '__main__':
router_factory = router.RouterFactory()
session_factory = wamp.RouterSessionFactory(router_factory)
session_factory.add(MyBackendComponent())
transport_factory = websocket.WampWebSocketServerFactory(session_factory,
debug=True,
debug_wamp=True)
AsyncIOMainLoop().install()
tornado_app.listen(80, "127.0.0.1")
loop = asyncio.get_event_loop()
coro = loop.create_server(transport_factory, "127.0.0.1", 8080)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
server.close()
loop.close()
Question: Is there the Right Way to make autobahn-wamp and tornado handlers listen on the same port?
My initial idea was to implement some kind of socket.socket wrapper and dispatch incoming messages there but it turned out to be awfully messy. I don't want to use any external proxies because the backend should be portable as much as possible.
Also I'm not asking anybody to implement it for me(but of course you can if you want to!) - only to know if somebody have already done something similar before diving into autobahn/tornado code.
Thanks in advance!
PS: Sorry for my poor English - it's not my mother tongue.

Tornado long connection

Can I realize "long connection(persistent connection, not long poll)" use tornado.web.RequestHandler? Because I need contain the connection, I need receive "heart message" which send by client, So, if tornado.web.RequestHandler can do it, How can I do? And have some demo like this?
Thanks!
RequestHandler isn't right for this, use WebSocketHandler instead. Here's an example app that receives a heartbeat from each client every second:
import tornado.ioloop
import tornado.web
import tornado.websocket
class HeartBeatReceiver(tornado.websocket.WebSocketHandler):
def open(self):
pass
def on_message(self, message):
print message
def on_close(self):
pass
class Main(tornado.web.RequestHandler):
def get(self):
# This could be a template, too.
self.write('''
<script>
function sendHeartBeat() {
ws.send("heartbeat");
setTimeout(sendHeartBeat, 1000);
}
ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = sendHeartBeat;
</script>''')
application = tornado.web.Application([
(r"/", Main),
(r"/websocket", HeartBeatReceiver),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Categories

Resources