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
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.8k
技術的負債解消で開発者の未来を開く- AIの力でコード刷新
kmd2kmd
0
120
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
280
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7.1k
これからAgentCoreを触る方へトレンドはGatewayです
har1101
5
420
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
130
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
610
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
420
Lessons from Spec-Driven Development
simas
PRO
0
230
JavaDoc 再入門
nagise
1
430
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
120
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
640
Featured
See All Featured
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
180
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.5k
A Tale of Four Properties
chriscoyier
163
24k
Paper Plane (Part 1)
katiecoart
PRO
0
9.3k
We Have a Design System, Now What?
morganepeng
55
8.2k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
210
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
350
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
72
40k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
170
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.4k
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
Into the Great Unknown - MozCon
thekraken
41
2.6k
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