Websockets talk at Rubyconf Uruguay 2010
-
Upload
ismael-celis -
Category
Technology
-
view
3.817 -
download
0
Transcript of Websockets talk at Rubyconf Uruguay 2010
WebSockets in Ruby(and things you can do with them)
Ismael Celis @ismasan github.com/ismasan
new bamboo
new-bamboo.co.uk pusherapp.com
Polling
Long-polling
Web socket
websockets.org/about.html
HTML5 Web sockets
Use cases
•Chat (lame)
•Stocks (lame but challenging)
•Games
•Presence
•Collaboration
•Real-time notifications
WebSockets DOM API<script type="text/javascript" charset="utf-8"> // Socket object var socket = new WebSocket('ws://some.host.com'); // Callbacks socket.onopen = function (evt) { alert('Socket connected: ' + evt.data) }; // Incoming server message socket.onmessage = function (evt) { alert( evt.data ) }; socket.onclose = function (evt) { alert('Connection terminated') // reconnect, etc. };</script>
WebSockets handshakeRequest
Response
GET /demo HTTP/1.1Host: example.comConnection: UpgradeSec-WebSocket-Key2: 12998 5 Y3 1 .P00Sec-WebSocket-Protocol: sampleUpgrade: WebSocketSec-WebSocket-Key1: 4 @1 46546xW%0l 1 5Origin: http://example.com
^n:ds[4U
HTTP/1.1 101 WebSocket Protocol HandshakeUpgrade: WebSocketConnection: UpgradeSec-WebSocket-Origin: http://example.comSec-WebSocket-Location: ws://example.com/demoSec-WebSocket-Protocol: sample
8jKS'y:G*Co,Wxa-
(draft 76)
"\x00Hello World\xff"
Messages
// Incoming server messagesocket.onmessage = function (evt) { alert( evt.data )};
// Send message to serversocket.send("Hello server, this is a new client");
(Ruby) WebSockets server
•Speak WebSockets (handshake)
•Keep connections alive
•Performant!
•Scalable
• Threads? Evented?
EventMachine rubyeventmachine.com
require 'eventmachine'
module EchoServer def receive_data(data) send_data data end end
EventMachine::run { EventMachine::start_server 'localhost', 8080, EchoServer puts 'running echo server on 8080'}
EM-WebSocket github.com/igrigorik/em-websocket
EventMachine.run { EventMachine::WebSocket.start(:host=>'0.0.0.0',:port=>8080) do |socket| socket.onopen { # publish message to the client socket.send 'Websocket connection open' } socket.onmessage {|msg| # echo message to client socket.send "Received message: #{msg}" } end}
Multicast - subscribers...@channel = Channel.new...
socket.onopen { @channel.subscribe socket}
socket.onmessage { |msg| @channel.send_message msg}
socket.onclose { @channel.unsubscribe socket}
Multicast - subscribers# Serversocket.onopen { @channel.subscribe socket}
class Channel def initialize @sockets = [] end def subscribe( socket ) @sockets << socket end ...end
Multicast - channelclass Channel ... def subscribe( socket ) @sockets << socket end def send_message( msg ) @sockets.each do |socket| socket.send msg end end def unsubscribe( socket ) @sockets.delete socket endend
Multicast - example
var socket = new WebSocket('ws://localhost:8080');
socket.onmessage = function( evt ) {$('<li>')
.text(evt.data) .appendTo('#messages');}
<ul id="messages"> </ul>
HTML
Javascript
Multicast - example
github.com/ismasan/websockets_examples
Pick your protocol
STOMP CONNECTlogin: <username>passcode: <passcode>
<message from=”[email protected]/ruby” to=”[email protected]/ruby”><body>Hey Jane!</body>
</message>
XMPP
Your own (JSON?)
Custom message format
JSON
Custom JSON format
{“event”: “user_connected”,
{“name” : “Ismael”,“total_users” : 10
}
}
Event name
Custom data
“data”:
Custom JSON format
{“event”: “user_message”,
{“message” : “Hello!”,“date” : “23 2010 21:12:28”
}
}
“data”:
Javascript wrapper
var socket = new FancyWebSocket('ws://localhost:8080');
...
socket.bind( 'user_connected', function (user_data) { // Add user to screen $('#connected_users').append('<li>' + user_data.name + '</li>');});
socket.bind( 'user_message', function (msg_data) { // Add message to screen $('#messages').append('<li>' + msg_data.message + '</li>');});
// Broadcast message - jQuery example
$('form#user_input').submit(function () { var msg = $(this).find('input[name=message]').val(); socket.send( 'user_message', {name: 'Ismael', message: msg} ); return false;});
Javascript wrapper
{“event” : “user_message”,
{“name” : “Ismael”,“message” : “hello!”
}}
“data” :
Implementation gist.github.com/299789var FancyWebSocket = function(url){ var conn = new WebSocket(url);
var callbacks = {};
this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); };
this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); };
// dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) };
conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)}
var dispatch = function(event_name, message){ var chain = callbacks[event_name]; if(typeof chain == 'undefined') return; // no callbacks for this event for(var i = 0; i < chain.length; i++){ chain[i]( message ) } }};
Implementation gist.github.com/299789
var FancyWebSocket = function(url){
var conn = new WebSocket(url);
var callbacks = {};
this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); };
this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); };
// dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) };
};
Ismael Celis
Implementation gist.github.com/299789
var FancyWebSocket = function(url){ var conn = new WebSocket(url);
var callbacks = {};
this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); };
this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); };
// dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) };
conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)}
var dispatch = function(event_name, message){ var chain = callbacks[event_name]; if(typeof chain == 'undefined') return; // no callbacks for this event for(var i = 0; i < chain.length; i++){ chain[i]( message ) } }};
// dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) };
conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)}
var dispatch = function(event_name, message){ var chain = callbacks[event_name]; if(typeof chain == 'undefined') return; // no callbacks for this event for(var i = 0; i < chain.length; i++){ chain[i]( message ) } }};
Implementation
WebSocket
gist.github.com/299789
var FancyWebSocket = function(url){
// dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) };
conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)}
var dispatch = function(event_name, message){ var chain = callbacks[event_name]; // no callbacks for this event if(typeof chain == 'undefined') return; for(var i = 0; i < chain.length; i++){ chain[i]( message ) } }};
Implementation gist.github.com/299789
socket.send( 'user_message', {name: 'Ismael', message: msg} );
Implementation gist.github.com/299789
this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); // <= send JSON data to socket server return this;};
Multicast - JSON data
github.com/ismasan/websockets_examples
Multicast - JSON data
$('#canvas').mousedown(function () { drawing = true;}).mouseup(function () { drawing = false;}).mousemove(function (evt) { if(drawing) { var point = [evt.pageX, evt.pageY]; socket.send('mousemove', point); }});
Multicast - JSON datavar ctx = document.getElementById('canvas').getContext('2d');ctx.lineWidth = 1;ctx.strokeStyle = '#ffffff';ctx.beginPath();ctx.moveTo(0, 0);
// Listen to other user's movessocket.bind('mousemove', function (point) { ctx.lineTo(point[0],point[1]); ctx.stroke();});
Activity dashboard
Rails Rumble dashboard
Scaling
?
pusherapp.com
pusherapp.com
Your appREST
Browsers
Websockets
Existing HTTP
pusherapp.com
pusherapp.com
<script src="http://js.pusherapp.com/1.6/pusher.min.js"></script>
<script type="text/javascript" charset="utf-8"> var socket = new Pusher('293d8ae77496e1fc053b'); </script>
pusherapp.com
var socket = new Pusher('293d8ae77496e1fc053b'); // Subscribe to channelsvar channel = socket.subscribe( 'test' ); channel.bind( 'new_message', function (data) { alert( data.message );})
pusherapp.com
// Subscribe to PRESENCE channelvar chat = socket.subscribe( 'presence-chat' ); // Listen to new memberschat.bind( 'pusher:member_added', function (member) { alert( member.user_data.name );})
require 'pusher'require 'sinatra'
post '/messages' do message = Message.create(params[:message]) Pusher['presence-chat'].trigger(:new_message, message.attributes) redirect '/messages'end
pusherapp.com
$ gem install pusher
pusherapp.com
github.com/
lifo/cramp
ismasan/websockets_examplesnewbamboo/rumbledash
blog.new-bamboo.co.ukblog.pusherapp.com