Share
Embed
Transcript
106 slides
-
Application HTTP Presentation TCP Session TCP Transport TCP Network IP
Data Link Physical -
Application HTTP Presentation TCP Session TCP Transport TCP Network IP
Data Link Physical the web stack -
• Headers • Content-Types • Caching • Authentication • Transactions
• Stateless this provides you with lots of useful tools -
Application HTTP Presentation TCP Session TCP Transport TCP Network IP
Data Link Physical -
Application HTTP Presentation TCP Session TCP Transport TCP Network IP
Data Link Physical tcp has sessions http throws them away -
var ws = new WebSocket('ws://example.org:8080/updates'); ws.onopen = function () {
ws.send('hello'); }; ws.onmessage = function (message) { console.log(message.data); }; -
JS INTERFACE • WebSocket • onopen • onmessage • onerror
• onclose • binaryType • MessageEvent • data -
• built on node.js • abstracts socket communication • supports
fallback transports -
Y U NO PHP? apache and php suck at long-running
connections -
$context = new ZMQContext(); $sock = $context->getSocket(ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $msg =
json_encode([ 'type' => 'debug', 'data' => ['foo', 'bar', 'baz'] ]); $sock->send($msg); basic php zmq client -
$context = new ZMQContext(); $sock = $context->getSocket(ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $msg =
json_encode([ 'type' => 'debug', 'data' => ['foo', 'bar', 'baz'] ]); $sock->send($msg); -
$context = new ZMQContext(); $sock = $context->getSocket(ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $msg =
json_encode([ 'type' => 'debug', 'data' => ['foo', 'bar', 'baz'] ]); $sock->send($msg); -
$context = new ZMQContext(); $sock = $context->getSocket(ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $msg =
json_encode([ 'type' => 'debug', 'data' => ['foo', 'bar', 'baz'] ]); $sock->send($msg); -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); listens for socket.io on port 8080 -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); listens for zmq on port 5555 -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); -
var io = require('socket.io').listen(8080), zmq = require('zmq'), sock = zmq.socket('sub');
sock.subscribe(''); sock.bind('tcp://*:5555'); sock.on('message', function (msg) { var event = JSON.parse(msg); io.sockets.emit(event.type, event.data); }); pass through messages from zmq to socket.io -
<script src="http://localhost:8080/socket.io/socket.io.js"> </script> <script> var socket = io.connect('http://localhost:8080'); socket.on('debug', function
(data) { console.log(data); }); </script> -
class CommentController extends Controller { public function createAction(Post $post) {
$comment = new Comment(); $comment->setPost($post); $request = $this->getRequest(); $form = $this->createForm(new CommentType(), $comment); $form->bindRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getEntityManager(); $em->persist($comment); $em->flush(); $renderedRow = $this->renderView( 'AcmeBlogBundle:Comment:row.html.twig', array('comment' => $comment) ); return new Response($renderedRow, 201); } return new Response('{}', 400, array('Content-Type' => 'application/json')); } } -
class CommentController extends Controller { public function createAction(Post $post) {
$comment = new Comment(); $comment->setPost($post); $request = $this->getRequest(); $form = $this->createForm(new CommentType(), $comment); $form->bindRequest($request); if ($form->isValid()) { $em = $this->getDoctrine()->getEntityManager(); $em->persist($comment); $em->flush(); $renderedRow = $this->renderView( 'AcmeBlogBundle:Comment:row.html.twig', array('comment' => $comment) ); return new Response($renderedRow, 201); } return new Response('{}', 400, array('Content-Type' => 'application/json')); } } ajax response -
var renderComment = function (renderedRow) { $('.comments').append(renderedRow); }; $('.comment-form form').submit(function
(event) { event.preventDefault(); var form = $(this), action = form.attr('action'), data = form.serialize(); $.post(action, data, function (comment) { renderComment(comment); resetForm(form); }); }); -
var renderComment = function (renderedRow) { $('.comments').append(renderedRow); }; $('.comment-form form').submit(function
(event) { event.preventDefault(); var form = $(this), action = form.attr('action'), data = form.serialize(); $.post(action, data, function (comment) { renderComment(comment); resetForm(form); }); }); -
var renderComment = function (renderedRow) { $('.comments').append(renderedRow); }; $('.comment-form form').submit(function
(event) { event.preventDefault(); var form = $(this), action = form.attr('action'), data = form.serialize(); $.post(action, data, function (comment) { renderComment(comment); resetForm(form); }); }); -
var renderComment = function (renderedRow) { $('.comments').append(renderedRow); }; $('.comment-form form').submit(function
(event) { event.preventDefault(); var form = $(this), action = form.attr('action'), data = form.serialize(); $.post(action, data, function (comment) { renderComment(comment); resetForm(form); }); }); -
private function publishCommentCreate(Comment $comment) { $context = new \ZMQContext(); $sock
= $context->getSocket(\ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $renderedRow = $this->renderView( 'AcmeBlogBundle:Comment:row.html.twig', array('comment' => $comment) ); $postId = $comment->getPost()->getId(); $msg = json_encode(array( 'type' => "post.$postId.comment.create", 'data' => $renderedRow, )); $sock->send($msg); } -
private function publishCommentCreate(Comment $comment) { $context = new \ZMQContext(); $sock
= $context->getSocket(\ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $renderedRow = $this->renderView( 'AcmeBlogBundle:Comment:row.html.twig', array('comment' => $comment) ); $postId = $comment->getPost()->getId(); $msg = json_encode(array( 'type' => "post.$postId.comment.create", 'data' => $renderedRow, )); $sock->send($msg); } -
private function publishCommentCreate(Comment $comment) { $context = new \ZMQContext(); $sock
= $context->getSocket(\ZMQ::SOCKET_PUB); $sock->connect('tcp://127.0.0.1:5555'); $renderedRow = $this->renderView( 'AcmeBlogBundle:Comment:row.html.twig', array('comment' => $comment) ); $postId = $comment->getPost()->getId(); $msg = json_encode(array( 'type' => "post.$postId.comment.create", 'data' => $renderedRow, )); $sock->send($msg); } per-post event -
<div class="messages"></div> <form> <input type="text" name="message"> <button type="submit">Send</button> </form> <script
src="http://localhost:8080/socket.io/socket.io.js"></script> <script src="jquery-1.7.min.js"></script> <script src="chat.js"></script> -
var socket = io.connect('http://localhost:8080'), messages = $('.messages'); socket.on('message', function (message)
{ var messageHtml = $('<div></div>').text(message.body); messages.append(messageHtml); messages.prop('scrollTop', 100000); }); -
var socket = io.connect('http://localhost:8080'), messages = $('.messages'); socket.on('message', function (message)
{ var messageHtml = $('<div></div>').text(message.body); messages.append(messageHtml); messages.prop('scrollTop', 100000); }); -
var form = $('form'); form.submit(function (event) { event.preventDefault(); var input
= form.find('*[name=message]'), body = input.val(); if (!body.length) { return; } socket.emit('message', { name: name, body: body }); input.val(''); }); -
var form = $('form'); form.submit(function (event) { event.preventDefault(); var input
= form.find('*[name=message]'), body = input.val(); if (!body.length) { return; } socket.emit('message', { name: name, body: body }); input.val(''); }); -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { socket.on('message', function
(message) { io.sockets.emit('message', message); }); }); -
var io = require('socket.io').listen(8080), messages; messages = []; io.sockets.on('connection', function
(socket) { messages.forEach(function (message) { socket.emit('message', message); }); socket.on('message', function (message) { io.sockets.emit('message', message); messages.push(message); }); }); -
var io = require('socket.io').listen(8080), messages; messages = []; io.sockets.on('connection', function
(socket) { messages.forEach(function (message) { socket.emit('message', message); }); socket.on('message', function (message) { io.sockets.emit('message', message); messages.push(message); }); }); buffer messages and send them -
var io = require('socket.io').listen(8080), bufferLimit = 15, messages; messages =
[]; io.sockets.on('connection', function (socket) { messages.forEach(function (message) { socket.emit('message', message); }); socket.on('message', function (message) { io.sockets.emit('message', message); messages.push(message); if (messages.length > bufferLimit) { messages = messages.splice(-bufferLimit, bufferLimit); } }); }); -
var io = require('socket.io').listen(8080), bufferLimit = 15, messages; messages =
[]; io.sockets.on('connection', function (socket) { messages.forEach(function (message) { socket.emit('message', message); }); socket.on('message', function (message) { io.sockets.emit('message', message); messages.push(message); if (messages.length > bufferLimit) { messages = messages.splice(-bufferLimit, bufferLimit); } }); }); limit the size of the buffer to not blow up the server -
Paintbrush Canvas mousedown mousemove mouseup WS mousedown mousemove mouseup Paintbrush
mousedown mousemove mouseup -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { var color
= '#'+Math.floor(Math.random()*16777215).toString(16); socket.emit('color', color); socket.on('mousedown', function (event) { event.source = socket.id; event.color = color; socket.broadcast.emit('mousedown', event); }); socket.on('mousemove', function (event) { event.source = socket.id; socket.broadcast.emit('mousemove', event); }); socket.on('mouseup', function (event) { event.source = socket.id; socket.broadcast.emit('mouseup', event); }); }); -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { var color
= '#'+Math.floor(Math.random()*16777215).toString(16); socket.emit('color', color); socket.on('mousedown', function (event) { event.source = socket.id; event.color = color; socket.broadcast.emit('mousedown', event); }); socket.on('mousemove', function (event) { event.source = socket.id; socket.broadcast.emit('mousemove', event); }); socket.on('mouseup', function (event) { event.source = socket.id; socket.broadcast.emit('mouseup', event); }); }); -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { var color
= '#'+Math.floor(Math.random()*16777215).toString(16); socket.emit('color', color); socket.on('mousedown', function (event) { event.source = socket.id; event.color = color; socket.broadcast.emit('mousedown', event); }); socket.on('mousemove', function (event) { event.source = socket.id; socket.broadcast.emit('mousemove', event); }); socket.on('mouseup', function (event) { event.source = socket.id; socket.broadcast.emit('mouseup', event); }); }); -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { var color
= '#'+Math.floor(Math.random()*16777215).toString(16); socket.emit('color', color); socket.on('mousedown', function (event) { event.source = socket.id; event.color = color; socket.broadcast.emit('mousedown', event); }); socket.on('mousemove', function (event) { event.source = socket.id; socket.broadcast.emit('mousemove', event); }); socket.on('mouseup', function (event) { event.source = socket.id; socket.broadcast.emit('mouseup', event); }); }); -
var io = require('socket.io').listen(8080); io.sockets.on('connection', function (socket) { var color
= '#'+Math.floor(Math.random()*16777215).toString(16); socket.emit('color', color); socket.on('mousedown', function (event) { event.source = socket.id; event.color = color; socket.broadcast.emit('mousedown', event); }); socket.on('mousemove', function (event) { event.source = socket.id; socket.broadcast.emit('mousemove', event); }); socket.on('mouseup', function (event) { event.source = socket.id; socket.broadcast.emit('mouseup', event); }); }); broadcast that shit -
PATTERNS • pub/sub • ajax-triggered messages • server authority •
server vs client-side templating -
SECURITY • Security model • Proxy poisoning • Authentication •
Encrypted transport (TLS/SSL) cross-site possible server receives origin header for auth, use cookies or pass a session id -
WEBSOCKET LIBRARIES • socket.io • node: Worlize/WebSocket-Node • ruby: igrigorik/em-websocket
• php: sebcode/php-websocketserver • js: gimite/web-socket-js that last one is the amazing flash client -
LINKS • tools.ietf.org/html/rfc6455 • dev.w3.org/html5/websockets • developer.mozilla.org/en/WebSockets • socket.io •
github.com/igorw/websockets-talk