I've got Raspberry Pi and I want to create a websocket server in python to which I could access remotely from a website.
I've got a few ideas to how I can make it, with the python libraries Tornado or WebSocket. In this way I've successfully created a python websocket server to which I access from a website... but locally.
So now, how can I make this server distant? I've seen that I need a ws:// or wss:// address, but I don't how to implement it in my first version's server.
server.py :
import os.path
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
#Tornado Folder Paths
settings = dict(
template_path = os.path.join(os.path.dirname(__file__), "templates"),
static_path = os.path.join(os.path.dirname(__file__), "static")
)
# Server port
PORT = 70
class MainHandler(tornado.web.RequestHandler):
def get(self):
print ("[HTTP](MainHandler) User Connected.")
self.render("index.html")
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print ('[WS] Connection was opened.')
def on_message(self, message):
print ('[WS] Incoming message: ' + message)
def on_close(self):
print ('[WS] Connection was closed.')
application = tornado.web.Application([
(r'/', MainHandler),
(r'/ws', WSHandler),
], **settings)
if __name__ == "__main__":
try:
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(PORT)
main_loop = tornado.ioloop.IOLoop.instance()
print ("Tornado Server started")
main_loop.start()
except:
print ("Exception triggered - Tornado Server stopped.")
Related
I am new to Tornado server in python and trying to a ping-pong at regular interval to the clients connected.
I saw something called websocket_ping_interval in tornado documentation but couldn't find any example as to how/when to use it.
I did the below using ioloop.PeriodicCallback but doesn't seem to be doing any ping.
import tornado.web
from tornado import ioloop
from terminado import TermSocket, SingleTermManager
from tornado import websocket
# BaseWebSocketHandler removed, because we need to track all opened
# sockets in the class. You could change this later.
class MeterInfo(websocket.WebSocketHandler):
"""Establish an websocket connection and send meter readings."""
opened_sockets = []
previous_meter_reading = 0
def open(self):
self.write_message('Connection Established.')
MeterInfo.opened_sockets.append(self)
def on_close(self):
"""Close the connection."""
self.write_message('bye')
MeterInfo.opened_sockets.remove(self)
#classmethod
def try_send_new_reading(cls):
"""Send new reading to all connected clients"""
new_reading = "text"
if new_reading == cls.previous_meter_reading:
return
cls.previous_meter_reading = new_reading
for socket in cls.opened_sockets:
socket.write_message({'A': new_reading})
if __name__ == '__main__':
term_manager = SingleTermManager(shell_command=['bash'])
handlers = [
(r"/websocket", TermSocket, {'term_manager': term_manager}),
(r"/()", tornado.web.StaticFileHandler, {'path': 'index.html'}),
(r"/(.*)", tornado.web.StaticFileHandler, {'path': '.'}),
]
app = tornado.web.Application(handlers)
app.listen(8010)
METER_CHECK_INTERVAL = 100 # ms
ioloop.PeriodicCallback(MeterInfo.try_send_new_reading,METER_CHECK_INTERVAL).start()
ioloop.IOLoop.instance().start()
All I need to do is to keep pinging the clients connected with something.
websocket_ping_interval is an application setting, so you pass it to the Application constructor:
app = tornado.web.Application(handlers, websocket_ping_interval=10)
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.
Good Day to all,
I have a websocket server written in python that serves clients changes done to a postgresql database table. Presently it uses the standard ws protocol. I would Like to implement SSL wss but there seems to be no documentation on the tornadoweb site. Any help appreciated!
Code as follows:
import tornado.web
import tornado.websocket
import tornado.ioloop
import tornado.httpserver
import threading
import select
import psycopg2
import psycopg2.extensions
# This is a global variable to store all connected clients
websockets = []
# Connect to DB to listen for notifications
conn = psycopg2.connect("host=xxx.xxx.xxx.xxx dbname=mydb user=xxx port=yyy")
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
curs = conn.cursor()
curs.execute("LISTEN notifier;")
# Create the websocket
class WebSocketHandler(tornado.websocket.WebSocketHandler):
#tornado.web.asynchronous
# Add to global variable the connected client
def open(self):
self.set_nodelay(True)
# on disconnect remove client
def on_close(self):
# Search for it in the list. If it matches with us (the client) then remove it as he has quit the con
try:
for websocket in websockets:
if websocket[0] == self:
websockets.remove(websocket)
except ValueError:
print ValueError
def on_message(self, message):
if self not in websockets:
websockets.append([self,message])
def on_pong(self, data):
print data
# This is the function that polls the database for changes, then it sends change to clients through websockets
def db_listener():
while 1:
if select.select([conn],[],[],5) == ([],[],[]):
for websocket in websockets:
# ping the client regularly to avoid disconnect
websocket[0].ping("ping")
else:
conn.poll()
while conn.notifies:
notify = conn.notifies.pop()
details = notify.payload.split(",")
if len(details) > 1:
for websocket in websockets:
if details[34] in websocket[1]:
websocket[0].write_message(notify.payload)
application = tornado.web.Application([
(r"/websocket", WebSocketHandler),
])
if __name__ == "__main__":
# Start a separate thread for every client so that we do not block the main websockets program!
threading.Thread(target=db_listener).start()
application.listen(5252)
tornado.ioloop.IOLoop.instance().start()
Any suggestions?
Thanks to all!!
try this:
server = tornado.httpserver.HTTPServer(application, ssl_options = {
"certfile": "path-to-crt-file",
"keyfile": "path-to-key-file",
})
server.listen(5252)
I want to run both a websocket and a flash policy file server on port 80 using Tornado. The reason for not wanting to run a server on the default port 843 is that it's often closed in corporate networks. Is it possible to do this and if so, how should I do this?
I tried the following structure, which does not seem to work: the websocket connection works, but the policy file request is not routed to the TCPHandler.
#!/usr/bin/python
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.gen
from tornado.options import define, options
from tornado.tcpserver import TCPServer
define("port", default=80, help="run on the given port", type=int)
class FlashPolicyServer(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):
policyFile = ""
self._stream.write(policyFile)
self._read_line()
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
pass
def on_message(self, message):
pass
def on_close(self):
pass
def main():
tornado.options.parse_command_line()
mainLoop = tornado.ioloop.IOLoop.instance()
app = tornado.web.Application(
handlers=[
(r"/websocket", WebSocketHandler),
(r"/", FlashPolicyServer)
], main_loop=mainLoop
)
httpServer = tornado.httpserver.HTTPServer(app)
httpServer.listen(options.port)
mainLoop.start()
if __name__ == "__main__":
main()
Any ideas? If this is not possible, would another idea be to serve the policy file via port 443?
There are two problems with your approach:
TCPServer is used like this:
server = TCPServer()
server.listen(6666)
IOLoop.instance().start()
That would do the trick, after you remove the (r"/", FlashPolicyServer) line.
But you want to use port 80.
However, you can't do that, because you need to have another HTTP server at that port.
So what you can do is to use nginx and set up port 80 as reverse proxying for /websocket, serving a Flash policy file otherwise.
I solved this by monkey-patching IOStream and HTTPServer:
def prepend_to_buffer(self, chunk):
self._read_buffer.appendleft(chunk)
self._read_buffer_size += len(chunk)
if self._read_buffer_size >= self.max_buffer_size:
logging.error("Reached maximum read buffer size")
self.close()
raise IOError("Reached maximum read buffer size")
return len(chunk)
def first_data_handler(self, data):
if data == '<policy-file-request/>\0':
self.stream.write(policy_file + '\0')
else:
self.stream.prepend_to_buffer(data)
tornado.httpserver.HTTPConnection(self.stream, self.address, self.request_callback,self.no_keep_alive, self.xheaders)
def handle_stream(self, stream, address):
self.stream = stream
self.address = address
self.stream.read_bytes(23, self.first_data_handler)
tornado.iostream.IOStream.prepend_to_buffer = prepend_to_buffer
tornado.httpserver.HTTPServer.first_data_handler = first_data_handler
tornado.httpserver.HTTPServer.handle_stream = handle_stream
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.