Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Aplicações Realtime com XMPP
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Rafael Macedo
May 17, 2014
Programming
220
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Aplicações Realtime com XMPP
Rafael Macedo
May 17, 2014
More Decks by Rafael Macedo
See All by Rafael Macedo
Modularização de código JS
macedorafael
0
250
TDC 2014 - Solucionando o problema de Uploads em Apps no Heroku
macedorafael
1
170
GuruSP - Solucionando o problema de Uploads em Apps no Heroku
macedorafael
2
120
Web in the cloud with ruby
macedorafael
2
400
Other Decks in Programming
See All in Programming
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
2.9k
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
170
初めてのKubernetes 本番運用でハマった話
oku053
0
130
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
270
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
540
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
350
継続モナドとリアクティブプログラミング
yukikurage
3
630
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
1
240
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
130
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
120
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
6.7k
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
0
300
Featured
See All Featured
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Information Architects: The Missing Link in Design Systems
soysaucechin
0
1k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Rails Girls Zürich Keynote
gr2m
96
14k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
Navigating Weather and Climate Data
rabernat
0
400
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
470
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Being A Developer After 40
akosma
91
590k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
330
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Transcript
Aplicações Realtime com XMPP
Rafael Macedo @macedorafael http://github.com/rafaelmacedo
Rafael Macedo @macedorafael http://github.com/rafaelmacedo
backend…
…com chapéu de front
…com chapéu de front by @jcemer
codeminer42.com
Estamos contratando codeminer42.com
XMPP
EXtensible Messaging and Presence Protocol
None
~ 300 extensões registrados na XSF (XMPP Standards Foundation) http://xmpp.org/xmpp-protocols/xmpp-extensions/
XEP (XMPP Extension Protocol) ✴ discussões na lista de email
✴ reviews ✴ votação ✴ testes de interoperabilidade
Um pouco de história…
Um pouco de história…
Um pouco de história… 1999 Projeto Jabber iniciado por Jeremie
Miller 2001 Fundação da Jabber Software Foundation (JSF) 2004 Publicação XMPP_1.0 RFC3920 RFC3921 RFC3922 RFC3923
Um pouco de história… 2007 Passou a se chamar XMPP
Standard Foundation(XSF) 2011 RFC 6121 RFC 6120 RFC 6122
None
Arquitetura XMPP
Endereçamento
XML Stanzas
Arquitetura XMPP Servidor Cliente c2s
Servidores
Clientes
✴ Jabber ID (JID) Endereçamento
[email protected]
/home
✴ Jabber ID (JID) Endereçamento
[email protected]
/home
✴ Jabber ID (JID) Endereçamento
[email protected]
/home
✴ Jabber ID (JID) Endereçamento
[email protected]
/home
XML Stanzas ✴ <message/> ✴ <presence/> ✴ <iq/>
<message/> <message from="
[email protected]
/cm42" to="
[email protected]
" type="chat"> <body>Hello World from XMPP!</body> </message>
<message from="
[email protected]
/cm42" to="
[email protected]
" type="chat"> <body>Hello World from XMPP!</body> </message> <message/>
<message from="
[email protected]
/cm42" to="
[email protected]
" type="chat"> <body>Hello World from XMPP!</body> </message> <message/>
<message from="
[email protected]
/cm42" to="
[email protected]
" type="chat"> <body>Hello World from XMPP!</body> </message> <message/>
chat error normal groupchat headline
<message from="
[email protected]
/cm42" to="
[email protected]
" type="chat"> <body>Hello World from XMPP!</body> </message> <message/>
<presence/> <presence type="away"> <show>away</show> <status>@ RSJS 2014</status> </presence>
<presence/> <presence type="away"> <show>away</show> <status>@ RSJS 2014</status> </presence>
<presence/> <presence type="away"> <show>away</show> <status>@ RSJS 2014</status> </presence>
<presence/> <presence type="away"> <show>away</show> <status>@ RSJS 2014</status> </presence>
<iq/> <iq to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/protocol/ disco#items"/> </iq>
<iq/> <iq to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/protocol/ disco#items"/> </iq>
<iq/> <iq to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/protocol/ disco#items"/> </iq>
<iq/> <iq to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/protocol/ disco#items"/> </iq> get
set result
<iq/> <iq to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/ protocol/disco#items"/> </iq>
<iq type="result" to="
[email protected]
/ 43048a3c-337c-4834-b412-40a424501a8" from="conference.taskie.org" id="rooms-info"> <query xmlns="http://jabber.org/protocol/ disco#items"> <item
jid="
[email protected]
" name="rsjs2014"/> </query> </iq>
XMPP na WEB
None
None
None
http://strophe.im/strophejs/ Strophe.js An XMPP library for JavaScript Strophe.js is an
XMPP library for JavaScript. It is primary purpose is to enable web-based, real-time XMPP applications that run in any browser.
✴ Documentada ✴ Otimizada ✴ Testada ✴ Chesspark, Seesmic, Yammer
Multi User Chat (MUC)
https://github.com/rafaelmacedo/xmpp-chat-rsjs2014
✴ XEP-0045: Multi-User Chat ✴ troca de mensagens no contexto
de uma sala ✴ banir usuários ✴ moderação ✴ … http://xmpp.org/extensions/xep-0045.html
Primeiros passos ✴ Criar conexão ✴ Conectar
Criar conexão
$("#login").on("click", function() { var connection = new Strophe.Connection( "http://taskie.org:5280/http-bind"); !
var jid = $("#jid").val() , passwd = $("#passwd").val(); ! connection.connect(jid, passwd, function(status) { if (status === Strophe.Status.CONNECTED) { alert("ihuuu connected!!!"); } else { if (status === Strophe.Status.AUTHFAIL) { alert("wrong passwd”); } } .... }); }); !
$("#login").on("click", function() { var connection = new Strophe.Connection( "http://taskie.org:5280/http- bind");
! var jid = $("#jid").val() , passwd = $("#passwd").val(); ! connection.connect(jid, passwd, function(status) { if (status === Strophe.Status.CONNECTED) { alert("ihuuu connected!!!"); } else { if (status === Strophe.Status.AUTHFAIL) { alert("wrong passwd”); } } .... }); }); !
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection ws:// ou wss://
=> WebSocket http:// ou https:// => BOSH
WEBSockets A B Iniciar conexão TCP GET /socket HTTP/1.1
\o/
✴xmpp over websocket ✴draft (http://tools.ietf.org/html/ draft-ietf-xmpp-websocket-06) ✴versão 6 (22/04/2014)
BOSH ✴Bidirectional-streams Over Syncrhonous HTTP
✴push/pull ✴eficiente ✴latência baixa
A B idle
A B idle
$("#login").on("click", function() { var connection = new Strophe.Connection( "http://taskie.org:5280/http-bind"); !
var jid = $("#jid").val() , passwd = $("#passwd").val(); ! connection.connect(jid, passwd, function(status) { if (status === Strophe.Status.CONNECTED) { alert("ihuuu connected!!!"); } else { if (status === Strophe.Status.AUTHFAIL) { alert("wrong passwd”); } } .... }); });
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection connect: function(jid, passwd,
callback )
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection connect: function(jid, passwd,
callback ) connecting authenticating authentication failed connected disconnecting disconnected
Strophe.Status .CONNECTING .AUTHENTICATING .CONNECTED .DISCONNECTING .DISCONNECTED .CONNFAIL .AUTHFAIL ! !
$("#login").on("click", function() { var connection = new Strophe.Connection( "http://taskie.org:5280/http-bind"); !
var jid = $("#jid").val() , passwd = $("#passwd").val(); ! connection.connect(jid, passwd, function(status) { if (status === Strophe.Status.CONNECTED) { alert("ihuuu connected!!!"); } else { if (status === Strophe.Status.AUTHFAIL) { alert("wrong passwd”); } } .... }); });
Conectado!! hora de entrar em alguma sala
rsjs2014 A B C D <presence/>
rsjs2014 A B C D <presence/> A <presence/> B <presence/>
C
rsjs2014 A B C D <presence/> D <presence/> D <presence/>
D <presence/> D
<presence from="
[email protected]
/home" to="
[email protected]
/rafaelmacedo"> <x xmlns="http://jabber.org/protocol/muc"/> </presence>
<presence from="
[email protected]
/home" to="
[email protected]
/ rafaelmacedo"> <x xmlns="http://jabber.org/protocol/muc"/> </presence>
<presence from="
[email protected]
/home" to="
[email protected]
/ rafaelmacedo"> <x xmlns="http://jabber.org/protocol/muc"/> </presence>
<presence from="
[email protected]
/home" to="
[email protected]
/ rafaelmacedo"> <x xmlns="http://jabber.org/protocol/muc"/> </presence>
$("#join").on("click", function() { var room = $("#room"), , nick =
$("#nick"); ! var presence = new Strophe.Builder(“presence”, { to: room + “@conference.taskie.org/” + nickname }) .c("x", {xmlns: "http://jabber.org/protocol/muc"}); ! connection.send(presence); }); Enviando informação de presença
$("#join").on("click", function() { var room = $("#room"), , nick =
$("#nick"); ! var presence = new Strophe.Builder(“presence”, { to: room + “@conference.taskie.org/” + nickname }) .c("x", {xmlns: "http://jabber.org/protocol/ muc"}); ! connection.send(presence); }); Enviando informação de presença
Strophe.Builder = function(name, attrs ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Builder
new Strophe.Builder( "message", { to: "
[email protected]
", type: "chat" } );
$pres(attrs) $msg(attrs) $iq(attrs)
new Strophe.Builder( "message", { to: "
[email protected]
", type: "chat" } );
$msg( { to: "
[email protected]
", type: "chat" });
c: function(name, attrs, text ) t: function(text) adicionando nó filho
adicionando conteúdo
new Strophe.Builder( "message", { to: "
[email protected]
", type: "chat" } );
$msg( { to: "
[email protected]
", type: "chat" }) .c("body") .t("Hello World!");
$("#join").on("click", function() { var room = $("#room"), , nick =
$("#nick"); ! var presence = $pres( { to: room + “@conference.taskie.org/” + nickname }) .c("x", {xmlns: "http://jabber.org/protocol/muc"}); ! connection.send(presence); }); Enviando informação de presença
$("#join").on("click", function() { var room = $("#room"), , nick =
$("#nick"); ! var presence = $pres( { to: room + “@conference.taskie.org/” + nickname }) .c("x", {xmlns: "http://jabber.org/protocol/muc"}); ! connection.send(presence); }); Enviando informação de presença
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection connect: function(jid, passwd,
callback ) send: function(elem)
Entrei?! mas cade todo mundo????
Recebendo informação de presença connection.addHandler(onPresence, null, "presence");
Strophe.Connection = function(service, options ) http://strophe.im/strophejs/doc/1.1.3/files/strophe- js.html#Strophe.Connection connect: function(jid, passwd,
callback ) send: function(elem) addHandler: function (handler, ns, name, type, id, from, options )
addTimedHandler: function(period, handler ) deleteTimedHandler: function( handRef) deleteHandler: function(handRef)
onPresence: function(presence) { var $presence = $(presence) , presenceType =
$presence.attr("type"); var from = $presence.attr("from") , room = Strophe.getBareJidFromJid(from); , nick = Strophe.getResourceFromJid(from); if (presenceType === "error") { connection.disconnect(); } else if (presenceType !== "unavailable") { participants.push({ nickname: nick }); } if (presenceType !== "error") { if ($presence.find("status[code='110']").length > 0) { if ($presence.find("status[code='210']").length > 0) { nick = Strophe.getResourceFromJid(from)); } } } return true; };
onPresence: function(presence) { var $presence = $(presence) , presenceType =
$presence.attr("type"); var from = $presence.attr("from") , room = Strophe.getBareJidFromJid(from); , nick = Strophe.getResourceFromJid(from); if (presenceType === "error") { connection.disconnect(); } else if (presenceType !== "unavailable") { participants.push({ nickname: nick }); } if (presenceType !== "error") { if ($presence.find("status[code='110']").length > 0) { if ($presence.find("status[code='210']").length > 0) { nick = Strophe.getResourceFromJid(from)); } } } return true; };
[email protected]
/rafaelmacedo resource
[email protected]
/rafaelmacedo Bare JID
onPresence: function(presence) { var $presence = $(presence) , presenceType =
$presence.attr("type"); var from = $presence.attr("from") , room = Strophe.getBareJidFromJid(from); , nick = Strophe.getResourceFromJid(from); if (presenceType === "error") { connection.disconnect(); } else if (presenceType !== "unavailable") { participants.push({ nickname: nick }); } if (presenceType !== "error") { if ($presence.find("status[code='110']").length > 0) { if ($presence.find("status[code='210']").length > 0) { nick = Strophe.getResourceFromJid(from)); } } } return true;
var from = $presence.attr("from") , room = Strophe.getBareJidFromJid(from); , nick
= Strophe.getResourceFromJid(from); if (presenceType === "error") { connection.disconnect(); } else if (presenceType !== "unavailable") { participants.push({ nickname: nick }); } if (presenceType !== "error") { if ($presence.find("status[code='110']").length > 0) { if ($presence.find("status[code='210']").length > 0) { nick = Strophe.getResourceFromJid(from)); } }
None
Enviando mensagens var message = $msg( { to: "
[email protected]
", type:
"groupchat" }); message.c("body").t("\o/"); connection.send(message);
Ouvindo mensagens connection.addHandler( onPublicMessage, null, "message", "groupchat");
onPublicMessage: function(message) { var $message = $(message); var from =
$message.attr("from") , room = Strophe.getBareJidFromJid(from) , nick = Strophe.getResourceFromJid(from); if (room === "
[email protected]
") { var msg = $message.children("body").text(); } return true; }
None
XMPP é a escolha certa? ✴ informação de presença ✴
notificações sem pooling ✴ custom payloads ✴ encriptação de canal
None
Obrigado Rafael Macedo @macedorafael rafaelmacedo.com
Obrigado Rafael Macedo @macedorafael rafaelmacedo.com ?
Referencias Professinal XMPP- Programming with JavaScript and jQuery XMPP -
The Definitive Guide
Procurando por salas ✴ iq stanza ✴ disco#items protocol <iq
to="conference.taskie.org" id="rooms-info" type="get"> <query xmlns="http://jabber.org/protocol/disco#items"/> </iq>
var iq = $iq( { to: "conference.taskie.org", type: "get", id:
"rooms-info" }); ! iq.c("query", { xmlns: "http://jabber.org/protocol/ disco#items" }); connection.send(iq);
connection.addHandler( onRoomsInfo, null, "iq", "result", "rooms-info");
onRoomsInfo: function(iq) { var $IQ = $(iq); var items =
$IQ.find("item") , rooms; ! rooms = _.map(items, function(item) { var $item = $(item); ! return { jid: $item.attr("jid"), name: $item.attr("name") }; }); ! return true; }
None