Autobahn
A package implementing web sockets and WAMP for python.
This package might be a little more up-to-date than the later examples. Autobahn not only implements the websocket protocol but also WAMP: the websocket application messaging protocol. WAMP uses json, and kees likes json. Also.. of all sites claiming to have a working tutorial, this is the only one that delivered, not a minor advantage. But let's not kid ourselves, i chose this because i love kraftwerk.
First get it :
| install |
|---|
| pip install autobahn
pip install twisted
|
twisted
Twisted is an event-driven networking engine written in Python, it is used heavily in at least the demo's.
For web development twisted provides :
- Twisted web, which is a simple (extremely) stable http server.
- Divmod Nevow, is a template toolkit.
- Divmod Mantissa, is an application server.
As i see it, twisted web will be the part that we use.
simple(st) example
Here is an example of an autobahn echo server :
| example |
|---|
| #!/usr/bin/python
# reactor implements an 'event' loop (internally a 'select')
from twisted.internet import reactor
from autobahn.websocket import WebSocketServerFactory, \
WebSocketServerProtocol, \
listenWS
# create a custom protocol subclassed from WebSocketServerProtocol
class EchoServerProtocol(WebSocketServerProtocol):
# with a handler for incoming messages
def onMessage(self, msg, binary):
self.sendMessage(msg, binary)
# detect standalone or imported version by checking __main__
if __name__ == '__main__':
factory = WebSocketServerFactory("ws://localhost:9000")
# and set our custom protocol
factory.protocol = EchoServerProtocol
# than listen on it
listenWS(factory)
# start the reactor and select
reactor.run()
|
Let's break that down step by step, if this script is ran standalone (it is) create a WebSocketServerFactory at the specified address. The reactor does the actual server loop, and normally you should call .install() on some specific reactor (for instance pollreactor) to change the default reactor (which just loops without doing anything). I can only assume WebSocketServerFactory() or listenWS() calls it's .install() internally, so assume the loop now handles the WebSocketServerFactory, with our custom protocol.
You could use a browser script like this :
| browser script |
|---|
| <!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var sock = null;
var wsuri = "ws://localhost:9000";
window.onload = function() {
sock = new WebSocket(wsuri);
sock.onopen = function() {
console.log("connected to " + wsuri);
}
sock.onclose = function(e) {
console.log("connection closed (" + e.code + ")");
}
sock.onmessage = function(e) {
console.log("message received: " + e.data);
}
};
function send() {
var msg = document.getElementById('message').value;
sock.send(msg);
};
</script>
</head>
<body>
<h1>WebSocket Echo Test</h1>
<form>
<p>
Message:
<input id="message" type="text" value="Hello, world!">
</p>
</form>
<button onclick='send();'>Send Message</button>
</body>
</html>
|
Put it somewhere reachable under apache and open it up in your browser. You should be able to send the message and see it coming in on the server.
We make a small input box, and connect it to the 'send()' function. onload() set's everything up before we can push and 'send()' so it opens the websocket to the running server, and we can send() our message onto this connected socket. This is all very C like, and i expect that from python:). As expected also, if you stop the server and start it again, the send message button does nothing until you refresh the page, thus reconnecting the socket.
Now extending this to use WAMP, see the next chapter
WAMP
server
The server gets augmented in a number of ways :
- We make it a WampServerFactory instead of a WebSocketServerFactory.
- We no longer use apache, but create a web server/site onto the current directory, on port 7070.
The new script :
| WampServerFactory |
|---|
| #!/usr/bin/python
import sys, math
from twisted.python import log
from twisted.internet import reactor, defer
from twisted.web.server import Site
from twisted.web.static import File
from autobahn.websocket import listenWS
from autobahn.wamp import exportRpc, WampServerFactory, WampServerProtocol
class Calc:
# this decorator makes this method ready for rpc
@exportRpc
def add(self, x, y):
return x + y
class SimpleServerProtocol(WampServerProtocol):
def onSessionOpen(self):
self.calc = Calc()
self.registerForRpc(self.calc, "http://example.com/simple/calc#")
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
log.startLogging(sys.stdout)
debug = True
else:
debug = False
factory = WampServerFactory("ws://localhost:9000", debugWamp = debug)
factory.protocol = SimpleServerProtocol
factory.setProtocolOptions(allowHixie76 = True)
listenWS(factory)
webdir = File(".")
web = Site(webdir)
reactor.listenTCP(7070, web)
reactor.run()
|
index.html will change as well :
| script |
|---|
| <!DOCTYPE html>
<html>
<head>
<!-- include AutobahnJS .. that's all you need -->
<script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>
<script>
// WAMP session object
var sess = null;
window.onload = function() {
var wsuri;
if (window.location.protocol === "file:") {
wsuri = "ws://localhost:9000";
} else {
wsuri = "ws://" + window.location.hostname + ":9000";
}
// connect to WAMP server
ab.connect(wsuri,
// WAMP session was established
function (session) {
sess = session;
console.log("Connected to " + wsuri);
test();
},
// WAMP session is gone
function (code, reason) {
sess = null;
if (code == ab.CONNECTION_UNSUPPORTED) {
window.location = "http://autobahn.ws/unsupportedbrowser";
} else {
alert(reason);
}
}
);
};
function test()
{
// establish a prefix, so we can abbreviate procedure URIs ..
sess.prefix("calc", "http://example.com/simple/calc#");
// call a function and log result on success
sess.call("calc:add", 23, 7).then(ab.log);
}
</script>
</head>
<body>
<h1>RPCs with AutobahnJS - Example 2</h1>
<noscript>
<span style="color: #f00; font-weight: bold;">
You need to turn on JavaScript.
</span>
</noscript>
<p>
Open development console (press F12) to watch.
</p>
</body>
</html>
|
Since there is no output, the script will ask you to hit F12 to open the console, and there you will see that the server returned 30: 23 and 7 added.
complete install on amazon
As a reminder, this is how you login ;)
| amazon install |
|---|
| cd ~/.ssh
ssh -i ec2solver.pem ubuntu@ec2-107-20-53-156.compute-1.amazonaws.com
|
Then some installation preliminaries :
| amazon install |
|---|
| sudo su
apt-get install subversion
svn co https://klopt.googlecode.com/svn/web/cycles
|
Now we need autobahn :
| autobahn |
|---|
| apt-get install python-pip
pip install autobahn
|