XMPP核心协议学习心得
综述:
XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
1.XMPP分为三大角色:
1.1 服务器
用于客户端间以XML流的形式进行连接和管理,也用于存储XMPP用户数据。
1.2 客户端
通过TCP协议连接到服务器,多个不同资源的客户端可以同时连接到服务器,每个不同资源的客户端通过XMPP地址的资源标识符来区分(比如<node@domain/home> 和 <node@domain/work>。建议的客户端和服务器连接的端口是 5222。
1.3 网关
一个特殊的服务器,用于与外部系统交互(如:EMAIL、SMS等),用于把XMPP协议转换给到外部或把接收到的消息转换成XMPP。
1.4
任意两个服务器之间的通信是可选的。如果被选择,那么这种通信应该通过 XML 流绑定到 TCP 连接上进行。建议服务器之间采用的的TCP连接端口为 5269。
所以正是这些服务器与服务器和客户及服务器间的连接,构建了整个通信网络。
2.地址空间
2.1 实体
实体格式:
jid = [ node "@" ] domain [ "/" resource ]
domain = fqdn / address-literal
fqdn = (sub-domain 1*("." sub-domain))
sub-domain = (internationalized domain label)
address-literal = IPv4address / IPv6address
一个 JID 的每个合法部分(节点名,域名,资源名)的长度不能超过 1023 字节。
2.2 域名
它通常代表网络的网关或者“主”服务器,其他实体通过连接它来实现 XML 转发和数据管理功能。然而,由一个域名标识引用的实体,并非总是一个服务器,它也可能是一个服务器的子域地址,提供额外的功能(比如多用户聊天服务,用户目录,或一个到外部消息系统的网关)。
3. XML流
格式:
| <stream>
| <presence>
| <show/>
| </presence>
| <message to='foo'>
| <body/>
| </message>
| <iq to='bar'>
| <query/>
| </iq>
| </stream>
3.1 流的安全性
在流被验证之间,实体不应该尝试通过流发送XML节;就算它这样做了,对方的实体也不能接受这些XML节,并且应该返回一个 <not-authorized/> 的流错误信息并且终止当前TCP连接上双方的XML流;注意,这仅仅是针对XML节(包含在缺省命名空间中的 <message/>, <presence/>, 和 <iq/> 元素),而不是指那些用于 TLS握手、SASL握手握手的流。
3.2 流的属性
|
初始化方发给接收方 |
接收方发给初始化方 |
to |
接收方的主机名 |
忽略 |
from |
忽略 |
接收方的主机名 |
id |
忽略 |
会话键值 |
xml:lang |
缺省语言 |
缺省语言 |
version |
支持XMPP 1.0 |
支持XMPP 1.0 |
3.3 版本支持
初始化实体必须在初始化流的头信息中把'版本'的值设置成它所支持的最高版本。
接收实体必须在应答流的头信息中把'版本'的值设置成初始化实体所提供的版本或它所支持的最高版本,取其中版本号较低的那一个。接收实体必须把主版本号和副版本号作为数字来比较,而不是对"主版本号.副版本号"这个字符串进行比较。
如果在应答流的头信息的版本号中至少有一个主版本号低于初始化流的头信息的版本号,并且如前所述,新版本的实体不能够和旧版本实体交互,初始化实体应该生成一个<unsupported-version/>的流错误信息并终止XML流和它的TCP连接。如果一个实体收到一个头信息中没有'version'属性的流,这个实体必须把对方实体的'version'当成'0.0'并且在它发送的应答流的头中也不应该包含'version'属性。
3.4 流错误
如果这个错误发生在流刚开始设置的时候,接收实体必须仍然发送一个开放标签 <stream> ,并在流元素中包含一个<error/>的子元素,然后发送一个关闭标签 </stream>,最后终止相应的TCP连接。在这种情况下,如果初始化实体在 'to' 属性中提供了一个未知的主机名,服务器应该在终止之前,先在流的头信息的 'from' 属性中提供一个服务器认证的主机名。
语法:
<stream:error>
<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
<text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>
Some special application diagnostic information!
</text>
<escape-your-data xmlns='application-ns'/>
</stream:error>
3.4.1 已经定义的error
<bad-format/> -- 实体已经发送XML但是不能被处理;这个错误可以(可以)被更多特定的XML相关的错误替换。(像是接口,下面是具体的实现);
<bad-namespace-prefix/> -- 实体发送的名字空间前缀不被支持,或者在一个需要某种前缀的元素中没有发送一个名字空间前缀。
<conflict/> -- 服务器正在关闭为这个实体激活的流,因为一个和已经存在的流有冲突的新的流已经被初始化。
<connection-timeout/> -- 实体已经很长时间没有通过这个流发生任何通信流量(可由一个本地服务策略来配置).
<host-gone/> -- 初始化实体在流的头信息中提供的'to'属性的值所指定的主机已经不再由这台服务器提供
<host-unknown/> -- 由初始化实体在流的头信息中提供的 'to' 属性的值和由服务器提供的主机名不一致。
<improper-addressing/> -- 一个在两台服务器之间传送的节缺少 'to' 或 'from' 属性(或者这个属性没有值).
<internal-server-error/> -- 服务器配置错误或者其他未定义的内部错误,使得服务器无法提供流服务.
<invalid-from/> -- 在'from'属性中提供的 JID 或 主机名地址,和认证的 JID不匹配 或服务器之间无法通过SASL(或回拨)协商出合法的域名,或客户端和服务器之间无法通过它进行认证和资源绑定。
<invalid-id/> -- 流 ID 或回拨 ID 是非法的或和以前提供的 ID 不一致.
<invalid-namespace/> -- 流名字空间和 "http://etherx.jabber.org/streams" 不相同或回拨名字空间和 "jabber:server:dialback" 不相同.
<invalid-xml/> -- 实体通过流发送了一个非法的XML给执行验证的服务器
<not-authorized/> -- 实体试图在流被验证之前发送数据或不被许可执行一个和流协商有关的动作,接收实体在发送错误信息之前不允许处理厌恶的节。
<policy-violation/> -- 实体违反了某些本地服务策略;服务器可以选择在 <text/> 元素或应用程序定义的错误条件(元素)中详细说明策略。
<remote-connection-failed/> -- 服务器无法正确连接到用于验证或授权的远程实体。
<resource-constraint/> -- 服务器缺乏必要的系统资源为流服务。
<restricted-xml/> -- 实体试图发送受限的XML特性,比如一个注释,处理指示,DTD,实体参考,或保留的字符.
<see-other-host/> -- 服务器将不提供服务给初始化实体但是把它重定向到另一台主机;服务器应该在<see-other-host/>元素的XML字符数据中指明替代服务器名或IP地址(它必须(必须)是合法的域名标识)。
<system-shutdown/> -- 服务器正在关机并且所有激活的流正在被关闭。
<undefined-condition/> -- 错误条件不在本文已定义的错误条件列表之中;这个错误条件应该仅用于"应用程序定义条件"元素.
<unsupported-encoding/> -- 初始化实体以一个服务器不不支持的编码方式编码了一个流
<unsupported-stanza-type/> -- 初始化实体发送了一个流的一级子元素但是服务器不支持.
<unsupported-version/> -- 由初始化实体在流的头信息中指定的'version'属性的值所指定的版本不被服务器支持;服务器可以在<text/>元素中指定一个它支持的版本号.
<xml-not-well-formed/> -- 初始化实体发送了一个不规范的XML。
3.5 实例
一个基本的 "会话":
C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
... encryption, authentication, and resource binding ...
C: <message from='juliet@example.com'
to='romeo@example.net'
xml:lang='en'>
C: <body>Art thou not Romeo, and a Montague?</body>
C: </message>
S: <message from='romeo@example.net'
to='juliet@example.com'
xml:lang='en'>
S: <body>Neither, fair saint, if either thee dislike.</body>
S: </message>
C: </stream:stream>
S: </stream:stream>
一个不成功的 "会话" :
C: <?xml version='1.0'?>
<stream:stream
to='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
S: <?xml version='1.0'?>
<stream:stream
from='example.com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>
... encryption, authentication, and resource binding ...
C: <message xml:lang='en'>
<body>Bad XML, no closing body tag!
</message>
S: <stream:error>
<xml-not-well-formed
xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
S: </stream:stream>