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
どこまでゆるくて許されるのか
tk3fftk
0
270
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
280
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
360
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
120
はてなアカウント基盤 State of the Union
cockscomb
1
1.1k
TSKaigi Night Talks 2026_TypeScriptでサプライチェーンの整合性を型に閉じ込める
geekplus_tech
0
420
スマートグラスで並列バイブコーディング
hyshu
0
270
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.8k
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
280
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
200
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7.1k
Contextとはなにか
chiroruxx
1
380
Featured
See All Featured
Balancing Empowerment & Direction
lara
6
1.2k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
590
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
320
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Producing Creativity
orderedlist
PRO
348
40k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
360
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.4k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
260
WCS-LA-2024
lcolladotor
0
660
Optimising Largest Contentful Paint
csswizardry
37
3.7k
Navigating Weather and Climate Data
rabernat
0
250
GitHub's CSS Performance
jonrohan
1033
470k
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