- Back to Home »
- Websocket: A Simple Example For IOT Project
Posted by : M yunus
Senin, 15 Februari 2016
Websocket is bi-directional
communication protocol which emerged recently, with the introduction of
HTML5. It enables full-duplex message based communication between client
and server. After connection is established, messages can be
transmitted, either client or server initiated. This means that you can
make a dynamic web page where changes occur in real time. In that way
Websockest communication presents a suitable protocol for IoT world
where changes are usually asynchronously occuring and number of clients
can be very large. Ok, sounds cool but what do I need to develop such
web pages? To implement Websocket communication in your app there are
some prerequisites on your server that has to be done. Apart from that,
clients have to run browsers that support Webosckets. In this article, a
simple example which includes Websocket is presented. The example is
tested on Raspbian (kernel ver. 3.12.21) and Debian (kernel ver.
2.6.39). In both cases Apache web server was used to deliver html pages
to the clients.
Server side
First thing to do is to choose webserver
implementation with Websocket protocol support. Among different
solutions such as Node.js (Socket.IO, WebSocket-Node, ws), Java (Jetty),
Ruby (EventMachine), Python (pywebsocket, Tornado), C++
(libwebsockets), .NET (SuperWebSocket) we have decided to pick Tornado,
an asynchronous webserver for python which is capable to simultaneously
handle more than 10k connections. To install Tornado we recommend to
use some package manager like pip or EasyInstall. We used pip, so get
it:
sudo apt-get install pip
After successful installation of pip package manager you can execute the following command to install Tornado:
pip install tornado
Alternatively, you can install Tornado manually (see here). Now your system is ready to use Websocketprotocol.
The next step is to write a simple Tornado web server app. Run your favorite text editor (we like nano):
sudo nano ws.py
and write some code:
import tornado.httpserver import tornado.websocket import tornado.ioloop import tornado.web class WSHandler(tornado.websocket.WebSocketHandler): def open(self): print 'user is connected.\n' def on_message(self, message): print 'received message: %s\n' %message self.write_message(message + ' OK') def on_close(self): print 'connection closed\n' application = tornado.web.Application([(r'/ws', WSHandler),]) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
As you can see, code consists of four parts:
- import of necessary modules,
- Websocket handler class,
- Initialization of Tornado app – web app configuration (websocket request handler) and
- Main program – setting up Tornado server, port definition and service start.
In WSHandler class we have defined three event handlers for basic communication:
- open – occurs when the connection is established,
- on_message – executed on every incomming message and
- on_close – handler triggered on the connection close event.
Here we are connecting appropriate event handler (WSHandler) with the URI to listen to (/ws’):
application = tornado.web.Application([(r'/ws', WSHandler),])
In the main, after defining the port number, we will call the start method of the Tornado server:
if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
You can finally start the server in the background:
python ws.py &
Now, your server is up and running, or
more precisely waiting for websocket connection. If you are connecting
over SSH, the server will be killed once you exit the session. To avoid
that, use
nohup
command to ignore hang up signal at the end of SSH connection:nohup python ws.py &
(Note: Make sure that port 8888 is
opened on your machine. Additionally, if you are running Tornado on a
machine in your local network which is behind the router, you have to
make port forwarding for websockets (port 8888). Otherwise this example
won't work for clients that connect from outside).
Client app
To implement websocket protocol all you
need is s a simple HTML web page with some JavaScript code. Well, we
used JQuery to enable websockets on the client side. Body of HTML
document has
<label>
element for connection status text, <textbox>
to input text and <button>
for sending messages:<!doctype html> <html> <head> <title>Websockets</title> <script src="http://code.jquery.com/jquery-2.0.0.js"></script> </head> <body> <h1>Websockets</h1> <label id="conn_text"></label><br /> <input type="text" id="input_text"/> <input type="submit" id="button" value="Send" /> <div id="messages_txt" />
Javascript code should be executed after
the page loads, i.e. after loading of JQuery script so it will be
placed inside anonymous function which is called when document.ready
event occurs:
<script> $(document).ready(function () { //script goes here }); </script> </body> </html>
We will create an instance of WebSocket class to handle all requests for our app:
var ws = new WebSocket("ws://example.com:8888/ws"); //change example.com with your host
We will define three event handlers:
onopen
, onmessage
and onclose
and they are invoked respectively: when connection is opened, when new
message arrives from server and when the websocket connection is closed.
onopen
notifies and onclose
alerts client about the connection status while onmessage
writes message from the server to the client’s web page:ws.onopen = function(evt) { var conn_status = document.getElementById('conn_text'); conn_status.innerHTML = "Connection status: Connected!" }; ws.onmessage = function(evt) { var newMessage = document.createElement('p'); newMessage.textContent = "Server: " + evt.data; document.getElementById('messages_txt').appendChild(newMessage); }; ws.onclose = function(evt) { alert ("Connection closed"); };
and message sending is performed when user clicks the button:
$("#button").click(function(evt) { evt.preventDefault(); var message = $("#input_text").val(); ws.send(message); var newMessage = document.createElement('p'); newMessage.textContent = "Client: " + message; document.getElementById('messages_txt').appendChild(newMessage); });
If you save the whole document on your
server you are ready to test websockets. Write address of your webpage
into the web browser’s address bar and you will be informed if
connecting to server was successful with the message inside the web
page: “Connection status: Connected!". Similar information, ”Connection
closed”, is alerted after closing the connection (for example if you
kill ws.py). On every message sent to the server, the server echoes back
that message but with “OK” in the end. You can see every message sent
from client or server in your web page:
The whole code of the web page should look like this:
<!doctype html> <html> <head> <title>Websocket</title> <script src="http://code.jquery.com/jquery-2.0.0.js"></script> </head> <body> <h1>Websocket</h1> <label id="conn_text"></label><br /> <input type="text" id="input_text"/> <input type="submit" id="button" value="Send" /><br /> <div id="messages_txt" /> <script> $(document).ready(function () { //change example.com with your IP or your host var ws = new WebSocket("ws://example.com:8888/ws"); ws.onopen = function(evt) { var conn_status = document.getElementById('conn_text'); conn_status.innerHTML = "Connection status: Connected!" }; ws.onmessage = function(evt) { var newMessage = document.createElement('p'); newMessage.textContent = "Server: " + evt.data; document.getElementById('messages_txt').appendChild(newMessage); }; ws.onclose = function(evt) { alert ("Connection closed"); }; $("#button").click(function(evt) { evt.preventDefault(); var message = $("#input_text").val(); ws.send(message); var newMessage = document.createElement('p'); newMessage.textContent = "Client: " + message; document.getElementById('messages_txt').appendChild(newMessage); }); }); </script> </body></html>
source: http://iot-projects.com/index.php?id=websocket-a-simple-example
Hi nice, tested on Python2 and Tornado 5 work fine also if I call the write_message from a separate thread (I'm collecting the clients in a global array to do that), but fail with Python3 and same Tornado release with error " There is no current event loop in thread '...' ".
BalasHapusDo you have some advice how to avoid it and get all working also in Pytgon3?
Thank
Here an example of the code to show the trouble;
BalasHapusimport tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
import threading
import time
clients=[]
exitGlobalFlag = False
# ----------------------------------------
# TORNADO HANDLER
# ----------------------------------------
class WSHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self):
global clients
print("New connection was opened from " + self.request.remote_ip)
print("Path: " + self.request.path)
self.write_message("Welcome to my websocket!")
if self not in clients:
clients.append(self)
def on_message(self, message):
print("Incoming message: "+message)
self.write_message("["+self.request.remote_ip+"] You said: " + message)
if (message == "QUIT"):
exitGlobalFlag = True
self.stopTornado()
def onCloseWrapper(self):
self.close()
self.on_close()
print("LOGOUT command received...")
def on_close(self):
print("Connection was closed...")
if self in clients:
clients.remove(self)
def stopTornado(self):
tornado.ioloop.IOLoop.instance().stop()
# ----------------------------------------
# Thread
# ----------------------------------------
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("Starting " + self.name)
while not(exitGlobalFlag):
for client in clients:
client.write_message("(Thread: " + self.name + ") Say hello!!!") # <-- EXCEPTION NO CURRENT EVENT LOOP
time.sleep(1)
# ----------------------------------------
# MAIN APPLICATION
# ----------------------------------------
if __name__ == "__main__":
# Run the thread
thread1 = myThread(1, "Thread-1", 1)
thread1.start()
# Set the tornado server
application = tornado.web.Application([(r'/ws', WSHandler),])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
myIP = socket.gethostbyname(socket.gethostname())
print("Websocket Server Started at: " + myIP)
tornado.ioloop.IOLoop.instance().start()
#tornado.ioloop.IOLoop.current().start()
# Exiting from application
print("Quit application!")
Hi,
BalasHapusafter some time I think to have solved:
https://stackoverflow.com/questions/51291726/tornado-5-0-2-with-python-3-5-3-accessing-tornado-from-separate-thread
Regards