SlideShare a Scribd company logo
Adventures in real-time web applications
Get Real!
Real time?
• In a real time application, all participants
receive content as soon as it is authored.
Party line
IRC
World of Warcraft
Real time is...
• Synchronous
• Social (sometimes)
• Collaborative
• Messy
Dispatched and Controlled
Real time web
• Any real time application that is accessed
via a web browser
Facebook
Twitter
EBS
• Gmail and Chat
• Google docs
• Any real-time feed
(news, stocks, weather,
class availability in registration system,
Lady Gaga’s Twitter feed)
• WebEx, GoTo Meeting, Adobe Connect,
etc. (??)
Others
Building real time web
apps is challenging
• HTTP is stateless
• Request / Response is its favorite song
• Simulating ongoing connection is expensive
• People still use IE
or
“The Wall”
is the
“Hello,World!”
of real time web programming.
of real time web programming.
of real time web programming.
of real time web programming.
of real time web programming.
Hello,Wall!
• Anything typed by anyone is seen by
everyone.
• Simply showing up on the page allows you
to start participating.
• Very low barrier to entry.
• Participants’ messages go to the dispatcher
• Participants need to get new messages
• Everyone has their own copy of the wall
How do people receive
changes to the wall?
• They could ask for them
• They could wait until someone calls
• But how do you call a browser?
• or appear to call
What are the options?
• Polling
• Long polling
• Hanging GET / infinite (forever) iFrame
• Flash Real Time Messaging Protocol (RTMP)
• Web sockets
• Server Sent Events (SSE)
• If all you’ve got is HTTP . . .
• Everything looks like a Request / Response
• This (anti)pattern is probably the most
common unexamined choice for keeping up
with status.
Polling
Polling
setInterval( function ( ) {
var server = ‘http://www.highedwebgetsreal.com’;
$.ajax({ url: server, data: {action: ‘getMessages’},
success: function(data) {
//Update the wall
chatFunctions.writeWall(data.value);
}, dataType: ‘json’}
);
}, 30000);
Polling
• AKAThe long-held connection
• Server programming waits to answer until
it has a meaningful answer
• The client either receives an event or times
out
• A new connection is established immdiately
Long Polling
Long Polling
(function poll() {
var server = ‘http://www.highedwebgetsreal.com’;
$.ajax({ url: server, data: {action: ‘getMessages’},
success: function(data) {
//Update the wall
chatFunctions.writeWall(data.value);
}, dataType: "json", complete: poll }
);
})();
Long polling
• The closest thing to a continuous
connection that HTTP may have to offer
• Leverages the fact that HTTP requests that
return chunked data do not need to
accurately report their size
• Unreliable support in IE
• A HACK!
Hanging GET or
infinite iFrame
infinite iFrame
Infinite iFrame
<iframe src="http://www.highedwebgetsreal.com/endless.html"
style=”position: absolute; left: -9999px;”>
<p>Your browser does not support iFrames.</p>
</iframe>
endless.html:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
23
<script>chatFunctions.writeToWall(‘Jane Bell: Anyone here yet’);</script>
1A
<script>chatFunctions.writeToWall(‘Aurora Smyth: Just got here. Hi!’);</script>
...
The infinite iFrame
• Client-side Flash object establishes stateful
and persistent connection with server
• Web page communicates via Javascript to
and from the Flash object
• Proprietary but fairly robust
Flash RTMP
script type='text/javascript' src='realTimeLib.js'></script>
div id=”wall”></div>
form>
<input type=”text” id='msg' />
<input type=”submit” id=”send” value=”Send” />
/form>
script type="text/javascript">
rtl = new realTimeLib();
rtl.setup({'RTMPrelay': 'RTMPrelay.swf'});
$(“#send”).click( function() {
rtl.send($(“#msg”).value());
});
function postToWall(data) {
// This function is called by rtl object’s swf when it receives a new post
$(“#wall”).append(data);
}
/script>
Flash RTMP
The story so far
• All of these techniques are work-arounds
and compromises
• Leverage side-effects of HTTP protocol and
browser implementations
• I.E. - not based on standards
• (except Flash RMTP, which has been
partially open-sourced by Adobe)
Web Sockets
• Web sockets is a proposal for a standard
that strives to solve the problem of server-
initiated messages to clients
• IETF RFC 6455
• In standards process at W3C
• Already old hat
Another channel
• Web Sockets work by requesting an
upgrade via a standard HTTP request
• The protocol gets changed to Web Socket,
which is a stateful TCP connection over
another port (often 443)
• Both client and server code then listens for
events on the channel and responds
accordingly.
Web sockets
are good listeners
are good listenersvar ws = new WebSocket("ws://www.highedwebgetsreal.com/chat");
ws.onopen = function() {
ws.send("{‘user’:‘David’}");
}
ws.onNewWallPost = function (evt) {
var received_msg = chatFunctions.parseServerEvt(evt.data);
chatFunctions.writeWall(received_msg.post);
}
$(“#sendBtn”).click(function() {
ws.send($(“#msgField”).value());
}
Look familiar?
Get Real: Adventures in realtime web apps
Ecapsulated and abstracted
graceful degradation
• Several libraries exist that enable
abstracted real time messaging
• They use the best technology available to
the client and fail over to the next best if
its missing
• Use Web Sockets, Flash, long polling,
or forever iFrame, in that order
Server side
• Node.js has become a very popular server
side implementation language
• It’s non-blocking and has a native event-
driven processing model
• It also lends itself to elegant symmetries
between server and client side code
Server side
• Juggernaut,WebSocket-Node and Socket.IO are
well tested Node libraries
• Socket.IO seems most mature and well maintained
• Nodejitsu.com hosts Node that you write
• Any framework that offers request routing works
well
• Ruby has EM-WebSocket, Socket.IO-rack, Cramp,
or just plain old Sinatra
Server side
• There are also services that take care of
the server for you and let you get started
building apps right away.
• Pusher.com is probably the most visible,
and maybe the most mature.
• peerbind.com is an interesting new-comer
Socket.IO Example
/app.js
ar app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
pp.listen(80);
unction handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
o.sockets.on('connection', function (socket) {
socket.on('set_nickname', function (data) {
socket.set('nickname', data, function () {
socket.emit('ready', data);
});
});
socket.on('post', function (data) {
var tmpName = "";
socket.get('nickname', function (err, data) {
tmpName = data;
});
socket.broadcast.emit('share', '<p><strong>' + tmpName + ':</strong> ' + data.msg + '</p>');
socket.emit('share', '<p><strong>(me):</strong> ' + data.msg + '</p>');
});
);
!doctype html>
html>
<head>
<meta charset="utf-8">
<meta charset=utf-8>
<title>Real time chat example</title>
</head>
<body>
<h1>Hello Chat!</h1>
<form id="getNickName">
<label for="nickname">Choose a nickname </label>
<input type="text" name="nickname">
<input type="submit" name="setNickname" value="Set it!">
</form>
</body>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript"
src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript"
src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0.beta6/handlebars.min.js"></script>
<script id="wallControl" type="text/x-handlebars-template">
<p>User Nickname: {{userNickName}}</p>
<div class="wall" />
<form>
<input type="text" name="msg" />
<input type="submit" name="send" value="send">
</form>
</script>
<script type="text/javascript">
$(document).ready( function() {
var socket = io.connect('/');
$("[name='setNickname']").click(function(event) {
event.preventDefault();
socket.emit("set_nickname", $("[name='nickname']").val());
$("body").append(theWall);
});
socket.on('ready', function (data) {
var theWall = Handlebars.compile($("#wallControl").html());
theWall = theWall({userNickName: data});
$("#getNickName").remove();
$("body").append(theWall);
});
$("[name='send']").live("click", function(event) {
socket.emit("post", { msg: $("[name='msg']").val() });
$("[name='msg']").val("");
event.preventDefault();
});
socket.on('share', function (data) {
$(".wall").append(data);
});
});
</script>
</html>
div class="image">
<div id="canvasDiv"></div>
/div>
p class="demoToolList"><button id="clearCanvas" type="button">Clear</button></p>
unction addClick(x, y, dragging, broadcast) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
if (broadcast) {
socket.emit("addClick", {x: x, y: y, dragging: dragging});
}
ocket.on('reAddClick', function (data) {
addClick(data.x, data.y, data.dragging, false);
);
unction redraw(click_x, click_y, click_drag, broadcast) {
clickX = click_x;
clickY = click_y;
clickDrag = clickDrag;
if (broadcast) {
socket.emit("redraw", {clickX: click_x, clickY: click_y, clickDrag: click_drag});
}
canvas.width = canvas.width; // Clears the canvas
context.strokeStyle = "#df4b26";
context.lineJoin = "round";
context.lineWidth = 5;
for(var i=0; i < click_x.length; i++) {
context.beginPath();
if(click_drag[i] && i){
context.moveTo(click_x[i-1], click_y[i-1]);
}else{
context.moveTo(click_x[i]-1, click_y[i]);
}
context.lineTo(click_x[i], click_y[i]);
context.closePath();
context.stroke();
}
ocket.on('reredraw', function (data) {
redraw(data.clickX, data.clickY, data.clickDrag, false);
);
More feature ideas easily
supported by Socket.IO
• Person to person IM sessions
• Chat rooms
• “Volatile” messages
• Blocking users
• Namespaces to multiplex single connections
Some things are server-sent
Server-Sent Events
(SSE)
• Web Hypertext Application Technology Working Group
(WHATWG)
• Opera was first to provide support in 2006
• Simpler messaging protocol
• No need to upgrade connection - Runs on HTTP/80
• All browsers now support SSE in their current versions
• Except IE (except with EventSource.js polyfill)
// Client-side Javascript
var source = new EventSource('/stream');
source.addEventListener('message', function(e){
console.log('Received a message:', e.data);
});
# Server-side solution using Sinatra
get '/stream', :provides => 'text/event-stream' do
stream :keep_open do |out|
connections << out
out.callback { connections.delete(out) }
end
end
post '/' do
connections.each { |out| out << "data: #{params[:msg]}nn" }
204 # response without entity body
end
David DeMello
ddd1@cornell.edu
@daviddemello
Questions?

More Related Content

Get Real: Adventures in realtime web apps

  • 1. Adventures in real-time web applications Get Real!
  • 2. Real time? • In a real time application, all participants receive content as soon as it is authored.
  • 4. IRC
  • 6. Real time is... • Synchronous • Social (sometimes) • Collaborative • Messy
  • 8. Real time web • Any real time application that is accessed via a web browser
  • 11. EBS
  • 12. • Gmail and Chat • Google docs • Any real-time feed (news, stocks, weather, class availability in registration system, Lady Gaga’s Twitter feed) • WebEx, GoTo Meeting, Adobe Connect, etc. (??) Others
  • 13. Building real time web apps is challenging • HTTP is stateless • Request / Response is its favorite song • Simulating ongoing connection is expensive • People still use IE
  • 14. or “The Wall” is the “Hello,World!” of real time web programming. of real time web programming. of real time web programming. of real time web programming. of real time web programming.
  • 15. Hello,Wall! • Anything typed by anyone is seen by everyone. • Simply showing up on the page allows you to start participating. • Very low barrier to entry.
  • 16. • Participants’ messages go to the dispatcher • Participants need to get new messages • Everyone has their own copy of the wall
  • 17. How do people receive changes to the wall? • They could ask for them • They could wait until someone calls • But how do you call a browser? • or appear to call
  • 18. What are the options?
  • 19. • Polling • Long polling • Hanging GET / infinite (forever) iFrame • Flash Real Time Messaging Protocol (RTMP) • Web sockets • Server Sent Events (SSE)
  • 20. • If all you’ve got is HTTP . . . • Everything looks like a Request / Response • This (anti)pattern is probably the most common unexamined choice for keeping up with status. Polling
  • 22. setInterval( function ( ) { var server = ‘http://www.highedwebgetsreal.com’; $.ajax({ url: server, data: {action: ‘getMessages’}, success: function(data) { //Update the wall chatFunctions.writeWall(data.value); }, dataType: ‘json’} ); }, 30000); Polling
  • 23. • AKAThe long-held connection • Server programming waits to answer until it has a meaningful answer • The client either receives an event or times out • A new connection is established immdiately Long Polling
  • 25. (function poll() { var server = ‘http://www.highedwebgetsreal.com’; $.ajax({ url: server, data: {action: ‘getMessages’}, success: function(data) { //Update the wall chatFunctions.writeWall(data.value); }, dataType: "json", complete: poll } ); })(); Long polling
  • 26. • The closest thing to a continuous connection that HTTP may have to offer • Leverages the fact that HTTP requests that return chunked data do not need to accurately report their size • Unreliable support in IE • A HACK! Hanging GET or infinite iFrame infinite iFrame
  • 28. <iframe src="http://www.highedwebgetsreal.com/endless.html" style=”position: absolute; left: -9999px;”> <p>Your browser does not support iFrames.</p> </iframe> endless.html: HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 23 <script>chatFunctions.writeToWall(‘Jane Bell: Anyone here yet’);</script> 1A <script>chatFunctions.writeToWall(‘Aurora Smyth: Just got here. Hi!’);</script> ... The infinite iFrame
  • 29. • Client-side Flash object establishes stateful and persistent connection with server • Web page communicates via Javascript to and from the Flash object • Proprietary but fairly robust Flash RTMP
  • 30. script type='text/javascript' src='realTimeLib.js'></script> div id=”wall”></div> form> <input type=”text” id='msg' /> <input type=”submit” id=”send” value=”Send” /> /form> script type="text/javascript"> rtl = new realTimeLib(); rtl.setup({'RTMPrelay': 'RTMPrelay.swf'}); $(“#send”).click( function() { rtl.send($(“#msg”).value()); }); function postToWall(data) { // This function is called by rtl object’s swf when it receives a new post $(“#wall”).append(data); } /script> Flash RTMP
  • 31. The story so far • All of these techniques are work-arounds and compromises • Leverage side-effects of HTTP protocol and browser implementations • I.E. - not based on standards • (except Flash RMTP, which has been partially open-sourced by Adobe)
  • 32. Web Sockets • Web sockets is a proposal for a standard that strives to solve the problem of server- initiated messages to clients • IETF RFC 6455 • In standards process at W3C • Already old hat
  • 33. Another channel • Web Sockets work by requesting an upgrade via a standard HTTP request • The protocol gets changed to Web Socket, which is a stateful TCP connection over another port (often 443) • Both client and server code then listens for events on the channel and responds accordingly.
  • 34. Web sockets are good listeners are good listenersvar ws = new WebSocket("ws://www.highedwebgetsreal.com/chat"); ws.onopen = function() { ws.send("{‘user’:‘David’}"); } ws.onNewWallPost = function (evt) { var received_msg = chatFunctions.parseServerEvt(evt.data); chatFunctions.writeWall(received_msg.post); } $(“#sendBtn”).click(function() { ws.send($(“#msgField”).value()); }
  • 37. Ecapsulated and abstracted graceful degradation • Several libraries exist that enable abstracted real time messaging • They use the best technology available to the client and fail over to the next best if its missing • Use Web Sockets, Flash, long polling, or forever iFrame, in that order
  • 38. Server side • Node.js has become a very popular server side implementation language • It’s non-blocking and has a native event- driven processing model • It also lends itself to elegant symmetries between server and client side code
  • 39. Server side • Juggernaut,WebSocket-Node and Socket.IO are well tested Node libraries • Socket.IO seems most mature and well maintained • Nodejitsu.com hosts Node that you write • Any framework that offers request routing works well • Ruby has EM-WebSocket, Socket.IO-rack, Cramp, or just plain old Sinatra
  • 40. Server side • There are also services that take care of the server for you and let you get started building apps right away. • Pusher.com is probably the most visible, and maybe the most mature. • peerbind.com is an interesting new-comer
  • 42. /app.js ar app = require('http').createServer(handler) , io = require('socket.io').listen(app) , fs = require('fs') pp.listen(80); unction handler (req, res) { fs.readFile(__dirname + '/index.html', function (err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); o.sockets.on('connection', function (socket) { socket.on('set_nickname', function (data) { socket.set('nickname', data, function () { socket.emit('ready', data); }); }); socket.on('post', function (data) { var tmpName = ""; socket.get('nickname', function (err, data) { tmpName = data; }); socket.broadcast.emit('share', '<p><strong>' + tmpName + ':</strong> ' + data.msg + '</p>'); socket.emit('share', '<p><strong>(me):</strong> ' + data.msg + '</p>'); }); );
  • 43. !doctype html> html> <head> <meta charset="utf-8"> <meta charset=utf-8> <title>Real time chat example</title> </head> <body> <h1>Hello Chat!</h1> <form id="getNickName"> <label for="nickname">Choose a nickname </label> <input type="text" name="nickname"> <input type="submit" name="setNickname" value="Set it!"> </form> </body> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0.beta6/handlebars.min.js"></script> <script id="wallControl" type="text/x-handlebars-template"> <p>User Nickname: {{userNickName}}</p> <div class="wall" /> <form> <input type="text" name="msg" /> <input type="submit" name="send" value="send"> </form> </script>
  • 44. <script type="text/javascript"> $(document).ready( function() { var socket = io.connect('/'); $("[name='setNickname']").click(function(event) { event.preventDefault(); socket.emit("set_nickname", $("[name='nickname']").val()); $("body").append(theWall); }); socket.on('ready', function (data) { var theWall = Handlebars.compile($("#wallControl").html()); theWall = theWall({userNickName: data}); $("#getNickName").remove(); $("body").append(theWall); }); $("[name='send']").live("click", function(event) { socket.emit("post", { msg: $("[name='msg']").val() }); $("[name='msg']").val(""); event.preventDefault(); }); socket.on('share', function (data) { $(".wall").append(data); }); }); </script> </html>
  • 45. div class="image"> <div id="canvasDiv"></div> /div> p class="demoToolList"><button id="clearCanvas" type="button">Clear</button></p> unction addClick(x, y, dragging, broadcast) { clickX.push(x); clickY.push(y); clickDrag.push(dragging); if (broadcast) { socket.emit("addClick", {x: x, y: y, dragging: dragging}); } ocket.on('reAddClick', function (data) { addClick(data.x, data.y, data.dragging, false); ); unction redraw(click_x, click_y, click_drag, broadcast) { clickX = click_x; clickY = click_y; clickDrag = clickDrag; if (broadcast) { socket.emit("redraw", {clickX: click_x, clickY: click_y, clickDrag: click_drag}); } canvas.width = canvas.width; // Clears the canvas context.strokeStyle = "#df4b26"; context.lineJoin = "round"; context.lineWidth = 5; for(var i=0; i < click_x.length; i++) { context.beginPath(); if(click_drag[i] && i){ context.moveTo(click_x[i-1], click_y[i-1]); }else{ context.moveTo(click_x[i]-1, click_y[i]); } context.lineTo(click_x[i], click_y[i]); context.closePath(); context.stroke(); } ocket.on('reredraw', function (data) { redraw(data.clickX, data.clickY, data.clickDrag, false); );
  • 46. More feature ideas easily supported by Socket.IO • Person to person IM sessions • Chat rooms • “Volatile” messages • Blocking users • Namespaces to multiplex single connections
  • 47. Some things are server-sent
  • 48. Server-Sent Events (SSE) • Web Hypertext Application Technology Working Group (WHATWG) • Opera was first to provide support in 2006 • Simpler messaging protocol • No need to upgrade connection - Runs on HTTP/80 • All browsers now support SSE in their current versions • Except IE (except with EventSource.js polyfill)
  • 49. // Client-side Javascript var source = new EventSource('/stream'); source.addEventListener('message', function(e){ console.log('Received a message:', e.data); }); # Server-side solution using Sinatra get '/stream', :provides => 'text/event-stream' do stream :keep_open do |out| connections << out out.callback { connections.delete(out) } end end post '/' do connections.each { |out| out << "data: #{params[:msg]}nn" } 204 # response without entity body end