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
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
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
230
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
420
自作OSでスライド発表する
uyuki234
1
3.9k
Android CLI
fornewid
0
120
AI時代の仕事技芸論〜ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ(スクフェス仙台 2026バージョン)
kuranuki
0
710
act2-costs.pdf
sumedhbala
0
120
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
190
PHP初心者セッション2026 〜生成AIでは見えない裏側を知る:今だからLAMPを通して仕組みを学ぶ〜
kashioka
0
650
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
17
9.3k
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.7k
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
180
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
2.9k
Featured
See All Featured
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.2k
Agile that works and the tools we love
rasmusluckow
331
22k
Git: the NoSQL Database
bkeepers
PRO
432
67k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.4k
Tell your own story through comics
letsgokoyo
1
1k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.6k
Speed Design
sergeychernyshev
33
1.9k
sira's awesome portfolio website redesign presentation
elsirapls
0
310
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Balancing Empowerment & Direction
lara
6
1.2k
Mobile First: as difficult as doing things right
swwweet
225
10k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.5k
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