XMPP message push实现研究
XMPP(可扩展消息处理现场协议)是基于XML的协议,它用于即时消息(IM)以及在线现场探测。利用该协议可以实现类似QQ那样的实时聊天工具。
优点:
l 有大量基于XMPP的开源IM应用;
l XMPP协议已经被IETF标准化,定义在RFC 3920及RFC 3921,以及众多的后续补充协议中。协议相对来说已经比较成熟;
l 安全性好,XMPP在Client-to-Server通信,和Server-to-Server通信中都使用TLS (Transport Layer Security)协议作为通信通道的加密方法,保证通信的安全。任何XMPP服务器可以独立于公众XMPP网络(例如在企业内部网络中),而使用SASL及TLS等技术更加增强了通信的安全性;
l 任何IM供应商在遵循XMPP协议下,都可与Google Talk实现连接。XMPP协议支持不同IM客户端之间互通,例如SMS(短信)、MSN、ICQ等;
l 分布式网络架构;XMPP网络的架构和电子邮件十分相像;XMPP核心协议通信方式是先创建一个stream,XMPP以TCP传递XML数据流,没有中央主服务器。任何人都可以运行自己的XMPP服务器,使个人及组织能够掌控他们的实时传讯体验。
l XMPP除了可用在实时通信的应用程序,还能用在网络管理、内容供稿、协同工具、文件共享、游戏、远程系统监控等。
缺点:
l 专门针对push message的开源成熟应用很少;并且大多数采用JAVA,对于资源受限的嵌入式系统,需要自行开发。
l 协议比较臃肿;对于推送一条消息后就结束和server连接的应用来说,需要额外发送的XMPP信令较多;
l XMPP 协议的方式被编码为一个单一的长的XML文件,因此无法提供修改二进制数据。因此,文件传输协议一样使用外部的HTTP。如果不可避免,XMPP协议还提供了带编码的文件传输的所有数据使用的Base64。至于其他二进制数据加密会话(encrypted conversations)或图形图标(graphic icons)以嵌入式使用相同的方法。
l 开源androidpn目前仅实现了推送给指定用户以及广播两项;还未支持tag多播选项,而tag多播基本上是我们应用主要使用的功能;因此这个功能待开发。
XMPP协议已被IETF标准化,并且已比较成熟;
RFC 3920 XMPP |
核心。定义了XMPP 协议框架下应用的网络架构,引入了XML Stream(XML 流)与XML Stanza(XML 节),并规定XMPP 协议在通信过程中使用的XML 标签。使用XML 标签从根本上说是协议开放性与扩展性的需要。此外,在通信的安全方面,把TLS 安全传输机制与SASL 认证机制引入到内核,与XMPP 进行无缝的连接,为协议的安全性、可靠性奠定了基础。Core 文档还规定了错误的定义及处理、XML 的使用规范、JID(Jabber Identifier,Jabber 标识符)的定义、命名规范等等。所以这是所有基于XMPP 协议的应用都必需支持的文档。 |
RFC 3921 |
用户成功登陆到服务器之后,发布更新自己的在线好友管理、发送即时聊天消息等业务。所有的这些业务都是通过三种基本的XML 节来完成的:IQ Stanza(IQ 节), Presence Stanza(Presence 节), Message Stanza(Message 节)。RFC3921 还对阻塞策略进行了定义,定义是多种阻塞方式。可以说,RFC3921 是RFC3920 的充分补充。两个文档结合起来,就形成了一个基本的即时通信协议平台,在这个平台上可以开发出各种各样的应用。 |
XEP-0030 服务搜索 |
一个强大的用来测定XMPP 网络中的其它实体所支持特性的协议。 |
XEP-0115 实体性能 |
XEP-0030 的一个通过即时出席的定制,可以实时改变交变广告功能。 |
XEP-0045 多人聊天 |
一组定义参与和管理多用户聊天室的协议,类似于Internet 的Relay Chat,具有很高的安全性。 |
XEP-0096 文件传输 |
定义了从一个XMPP 实体到另一个的文件传输。 |
XEP-0124 HTTP 绑定 |
将XMPP 绑定到HTTP 而不是TCP,主要用于不能够持久的维持与服务器TCP 连接的设备。 |
XEP-0166 Jingle |
规定了多媒体通信协商的整体架构。 |
XEP-0167 Jingle Audio Content Description Format |
定义了从一个XMPP 实体到另一个的语音传输过程。 |
XEP-0176 Jingle ICE(Interactive Connectivity Establishment)Transport |
ICE传输机制,文件解决了如何让防火墙或是NAT(Network Address Translation)保护下的实体建立连接的问题。 |
XEP-0177 Jingle Raw UDP Transport |
纯UDP 传输机制,文件讲述了如何在没有防火墙且在同一网络下建立连接的。 |
XEP-0180 Jingle Video Content Description Format |
定义了从一个XMPP 实体到另一个的视频传输过程。 |
XEP-0181 Jingle DTMF |
Dual Tone Multi-Frequency |
XEP-0183 Jingle Telepathy Transport Method |
|
l XMPP客户端:客户端通过 TCP 连接直接连到服务器,并通过XMPP获得由服务器和任何相关的服务所提供的全部功能;例如MSN,ICQ客户端。
l XMPP服务器:管理客户端连接/认证/登录,客户端之间的消息转发,保存客户端的用户列表等信息。
l XMPP网关:实现XMPP协议消息和其他网络协议消息之间的转换,实现XMPP客户端和其他非XMPP客户端(例如MSN客户端)之间的通讯。
这里我们进行Openfire 、Spark客户端、pidgin客户端的交互测试;对应的是类似QQ的两个IM客户端进行实时文字聊天。
Spark客户端运行在linux上,pidgin客户端运行在windows xp上,下面演示两者和Openfire之间的信令交互过程。
从中可以看出开始发送第一条用户消息前,需要进行比较多的信令交互过程。并且客户端需要多次发送初始化流给服务器;
步骤 |
信令举例 |
备注 |
1 |
客户端初始化流给服务器: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'> |
|
2 |
服务器发送一个流标签给客户端作为应答: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_123' from='example.com' version='1.0'> |
|
3 |
服务器发送 STARTTLS 范围给客户端(包括验证机制和任何其他流特性): <stream:features> <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls> <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism>DIGEST-MD5</mechanism> <mechanism>PLAIN</mechanism> </mechanisms> </stream:features> |
|
4 |
客户端发送 STARTTLS 命令给服务器: <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/> |
|
5 |
服务器通知客户端可以继续进行: <proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/> |
成功 |
服务器通知客户端 TLS 握手失败并关闭流和TCP连接: <failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/> </stream:stream> |
失败 |
|
6 |
客户端和服务器尝试通过已有的TCP连接完成 TLS 握手 |
|
7 |
如果 TLS 握手成功, 客户端初始化一个新的流给服务器: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'> |
成功 |
如果 TLS 握手不成功, 服务器关闭 TCP 连接. |
失败 |
|
8 |
服务器发送一个流头信息应答客户端,其中包括任何可用的流特性: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='c2s_234' version='1.0'> <stream:features> <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> <mechanism>DIGEST-MD5</mechanism> <mechanism>PLAIN</mechanism> <mechanism>EXTERNAL</mechanism> </mechanisms> </stream:features> |
|
9 |
客户端选择一个验证机制: <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/> |
|
10 |
服务器发送一个 [BASE64] 编码的挑战给客户端: <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==</challenge> |
成功 |
服务器返回一个错误给客户端: <failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><incorrect-encoding/></failure></stream:stream> |
失败 |
|
11 |
客户端发送一个[BASE64]编码的回应这个挑战: <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo YXJzZXQ9dXRmLTgK</response> |
|
12 |
服务器发送另一个[BASE64]编码的挑战给客户端: <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=</challenge> |
成功 |
服务器返回一个错误给客户端: <failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure></stream:stream> |
失败 |
|
13 |
客户端应答这个挑战: <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> |
|
14 |
服务器通知客户端验证成功: <success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> |
成功 |
服务器通知客户端验证失败: <failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure></stream:stream> |
失败 |
|
15 |
客户端发起一个新的流给服务器: <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'> |
|
16 |
服务器发送一个流头信息回应客户端,并附上任何可用的特性(或空的features元素): <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345' from='example.com' version='1.0'> <stream:features> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> </stream:features> |
|
|
|
|
使用XMPP进行消息推送应用,在网上提到最多的就是androidpn;在信令流程上,和前面实时聊天客户端的基本上是一致的。