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
240
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
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
190
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
160
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
130
The NotImplementedError Problem in Ruby
koic
1
630
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
3
1.2k
開発体験を左右するライブラリの API 設計 - GraphQL スキーマ構築ライブラリから考える #tskaigi
izumin5210
2
1.6k
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
120
Swiftのレキシカルスコープ管理
kntkymt
0
210
oxlintはeslint/typescript-eslintを置き換えられるのか
shomafujita
2
320
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
140
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
240
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
4.5k
Featured
See All Featured
It's Worth the Effort
3n
188
29k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
23k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
220
Ruling the World: When Life Gets Gamed
codingconduct
0
250
From π to Pie charts
rasagy
0
200
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
190
GitHub's CSS Performance
jonrohan
1033
470k
Documentation Writing (for coders)
carmenintech
77
5.4k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
380
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