Weitere ähnliche Inhalte Ähnlich wie Websockets talk at Rubyconf Uruguay 2010 (20) Kürzlich hochgeladen (20) Websockets talk at Rubyconf Uruguay 20101. WebSockets in Ruby
(and things you can do with them)
Ismael Celis @ismasan github.com/ismasan
new bamboo
new-bamboo.co.uk pusherapp.com
4. 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>
5. WebSockets handshake
Request
Response
GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Sec-WebSocket-Protocol: sample
Upgrade: WebSocket
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
Origin: http://example.com
^n:ds[4U
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Location: ws://example.com/demo
Sec-WebSocket-Protocol: sample
8jKS'y:G*Co,Wxa-
(draft 76)
10. Multicast - subscribers
...
@channel = Channel.new
...
socket.onopen {
@channel.subscribe socket
}
socket.onmessage { |msg|
@channel.send_message msg
}
socket.onclose {
@channel.unsubscribe socket
}
11. Multicast - subscribers
# Server
socket.onopen {
@channel.subscribe socket
}
class Channel
def initialize
@sockets = []
end
def subscribe( socket )
@sockets << socket
end
...
end
12. Multicast - channel
class 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
end
end
13. 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
15. Pick your protocol
STOMP CONNECT
login: <username>
passcode: <passcode>
<message
from=”john@server.com/ruby”
to=”jane@server.com/ruby”>
<body>Hey Jane!</body>
</message>
XMPP
Your own (JSON?)
19. 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>');
});
20. // 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” :
21. 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 )
}
}
};
22. 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 );
};
23. 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_d
conn.send( payload );
};
// dispatch to the right handlers
conn.onmessage = function(evt){
var json = JSON.parse(evt.data)
24. // 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
25. 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
26. 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;
};
28. 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);
}
});
29. Multicast - JSON data
var ctx = document.getElementById
('canvas').getContext('2d');
ctx.lineWidth = 1;
ctx.strokeStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(0, 0);
// Listen to other user's moves
socket.bind('mousemove', function (point) {
ctx.lineTo(point[0],point[1]);
ctx.stroke();
});
37. pusherapp.com
var socket = new Pusher('293d8ae77496e1fc053b');
// Subscribe to channels
var channel = socket.subscribe( 'test' );
channel.bind( 'new_message', function (data) {
alert( data.message );
})
38. pusherapp.com
// Subscribe to PRESENCE channel
var chat = socket.subscribe( 'presence-chat' );
// Listen to new members
chat.bind( 'pusher:member_added', function (member) {
alert( member.user_data.name );
})
39. 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