1.介绍

       XMPP Core 用XMPP定义了流的XML技术(也就是流的建立和终止,包含认证和加密)。可是核心的XMPP协议并没有为管理一个灵活的XML流提供工具。

       Stream Management背后的基本概念是。初始化的实体(一个服务端或者client)和接收的实体(一个服务端)能够为更灵活的管理stream交换“命令”。以下两条 Stream Management的特性被广泛的关注。由于它们能够提高网络的可靠性和终端用户的体验:

     *Stanza确认(Stanza Acknowledgements) ——  可以确认一段或者一系列Stanza是否已被某一方接收。

     *流恢复(Stream Resumption) ——  可以迅速的恢复(resume)一个已经被终止的流。

    Stream management 用较的短XML元素实现了这些特性,这些XML元素是在流的标准上的。这些元素并非XMPP意义上的“stanzas”(也就是说,不是<iq/>, <message/>, or <presence/>这种stanzas,stanzas在RFC 6120中有定义)。它们不会在Stream management中被counted或者被acked,由于它们是为管理stanzas本身而存在的。

      Stream management是在XML流的标准上使用的。检查一个给定的流TCP的连通性的时候。特别推荐使用whitespace keepalives(见RFC 6120)、XMPP Ping (XEP-0199) 或者TCP keepalives。对照Stream management,高级消息处理Advanced Message Processing (XEP-0079)和消息回执Message Delivery Receipts (XEP-0184)。定义了ack,它能够通过多个流实现端对端的传输;这些特性在一些特殊情况中是实用的,可是不是必需去检查在两个xmpp实体之间直接传递的流。

注:Stream management能够用于服务端到服务端、client到服务端的流。可是,为了方便,本规范仅仅讨论client到服务端的流。

相同的原则也适用于server到server的流。(在本文档中,以“C:”开头的都是由client发送的,由“S:“开头的都是由server发送的)。

2. 流特性(Stream Feature)

服务端返回一个流的header给client,紧接着要发送流的features。这些features包含一个<sm/>元素“urn:xmpp:sm:3”的命名空间namespace(见Namespace Versioning 关于版本添加的可能性)。

注:client不能negotiate(谈判,商量;转让;越过) stream management,除非client已经身份验证而且绑定了一个资源。见以下的特定的限制条件。

例1:server发送新的流的header和流的features。

S: <stream:stream
from='example.com'
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
version='1.0'>

S: <stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
<sm xmlns='urn:xmpp:sm:3'/>
</stream:features>

3. 启动Stream Management

    要启动使用Stream Management,client发送一个<enable/> 命令给服务端。

例2:client启动Stream Management

C: <enable xmlns='urn:xmpp:sm:3’/>

    假设client想被同意恢复流,必须包含一个boolean类型的“resume”属性。默觉得false。恢复先前的一个会话(section),请參阅本文档的恢复部分。

    <enable/>元素能够包括一个“max”属性,用来指定client最优的最大恢复时间,以秒为单位。

    一旦受到启动请求,服务端必须回复一个<enabled/>元素,或者一个<failed/>,注明 'urn:xmpp:sm:3' 的命名空间namespace。

<failed/>元素表示在建立stream management 会话(session)中存在一个问题。

<enabled/>元素表示成功的建立了stream management 会话。

例3:服务端启动stream management

S: <enabled xmlns='urn:xmpp:sm:3’/>

    然后能够使用stream management的特性定义以下的内容。

    假设服务端同意会话恢复。服务端必须包含一个resume属性。值为”ture“或者”1“。

例4:服务端启动同意会话恢复的stream management

S: <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true’/>

      <enabled/>元素能够包括一个”max“属性,用来指定服务端特定的最大恢复时间。

      <enabled/>属性能够包括一个”location“属性,用来指定服务端的IP地址或者域名(端口可选)又一次连接。见RFC6120的4.9.3.19部分(即,"domainpart:port", IPv6地址在方括号中[…]。见RFC5952)。假设重连那个location失败了,标准的XMPP重连算法在RFC6120中有具体说明。

      client不能尝试negotiate stream management,除非通过了身份验证;也就是说client不能发送一个<enabled/>元素直到完毕了身份验证。(比如,SASL,Non-SASL Authentication (XEP-0078)或者 Server Dialback (XEP-0220)已经成功完毕)。

      对于client到服务端的连接。在没有完毕资源绑定前,client禁止启动流管理,除非正在恢复一个之前的会话(见Resumption)。

      服务端应该实施这个命令,假设这个规则被违反了。要返回一个<fail/>元素。


例5:假设client试图在未绑定资源前启动stream management,服务端返回错误

S: <failed xmlns='urn:xmpp:sm:3'>
<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

</failed>


4. Acks

启动流管理(stream management)之后,client或者服务端能够在随意时间通过流发送ack元素。ack元素能够是:

    <a/>元素用于回答一个确认收到的请求或者发送一个未被请求的ack。

    <r/>元素用于请求收到节(stanzas)的确认信息。

属性定义例如以下:

    'h'属性标识最后处理的节(即,server确认收到最后一节)。

    一个<a/>元素必须具备一个”h“属性。

    <r/>元素没有已定义属性。

定义:确认一条之前收到的ack元素。节是被server处理后再发送出去的。对于服务端的处理,我们是指服务端接管一条或多条节(如。直接处理节。将节传给本地的一个实体。比方同样server下的还有一个client,或者将节发送给其它server的远程实体);一个节在确认被服务端处理之前,发送方都要对节负责(比如,假设没有被服务端确认处理。client要重发这个节或者生成一个错误)。

       收到一个<r/>元素的回执并不意味着新的节已经传送给对方,仅仅有收到一个<a/>元素的回执。而且包括“h”属性已经增长了,才说明这个新的节已经被处理过。

      “h”值在流管理中被启动或被请求启动时,是从零開始的。当第一个节被处理的时候,“h”值添加为1。并随着接下来新的节被处理,“h”值不断添加。在一些极端情况下,在流管理对话中被处理的节的数量,超出了无符号整型数所表示的最大数(2的32次方)XML Schema Part 2  [10],“h”值应该被置为0,而不是2的32次方。

       注意:不论什么给定的流,都有两个“h”值。一个被client用于跟踪节是否被服务端处理,还有一个被服务端用于跟踪节是否被处理自client。当client向服务端发送<enable/>时,client初始化它的“h”值为零,相同服务端发送<enable/>给client时,也初始化它的“h”值为零(服务端会马上回复<enable/>,同一时候设置它的计数器为零)。初始化后。client依据处理的节的数目不断添加“h”值,服务端也对应不断添加“h”值。

     以下的带凝视的样例。包含client发的一段消息。一个确认接收的请求,一个节的ack。

例6:Simple stanza acking

C: <enable xmlns='urn:xmpp:sm:3'/>

<!-- Client sets outbound count to zero.(client设置outbound为0) -->

C: <message from='laurence@example.net/churchyard'
to='juliet@example.com'
xml:lang='en'>
<body>
I'll send a friar with speed, to Mantua,
with my letters to thy lord.
</body>
</message>

<!-- Note that client need not wait for a response.(注意client不须要等待回复) -->

S: <enabled xmlns='urn:xmpp:sm:3'/>

<!--
Server receives enable, and responds,
setting both inbound and outbound counts
to zero. (服务端接收enable,回复client,设置inbound和outboard数为零)

In addition, client sets inbound count to zero. (另外,client设置inbound为零)
-->

C: <r xmlns='urn:xmpp:sm:3'/>

S: <a xmlns='urn:xmpp:sm:3' h='1'/>

       当一个<r/>元素(“request”)被收到时。接收方必须通过给发送方发送一个带h值的<a/>元素的回执确认,h值大小为<r/>元素接受方处理的节的数量。

回复必须在接收到<r/>元素后马上发送。除了超时以外的不论什么情况,都不能保留。比如,有时候client的网速比較慢,它在确认收到前可能希望在一段时间内收集非常多节,服务端可能希望降低传入的节。

发送方无需等待继续发送节的ack。

       不论什么一方都能够在不论什么时间发送一条<a/>元素(如。接收完确切数目的节后。或者一个确定的时间段过后),即便是未收到对方发送的<r/>元素。

       当一方收到一个<a/>元素,必须返回一个h值作为当前流最后处理的那条出站节(outbound stanza)的序列号。

       假设一个流结束了,也没有在<enable/>元素中指定的时间内恢复。序列号和不论什么相关的状态都能够被两方丢弃。

在会话状态被丢弃前,应该对不论什么未处理的节(即在收到近期的h值后发送的节)採取替代措施。

           *服务端应该採用同样的方式处理未确认的节。对于发送到不可用资源的节,服务端应该给发送方返回一个错误。或者把该节提交给离线仓库存储。

           *面向用户的client应该悄悄地(不用通知用户)通过连接又一次发送节,或者通过一个合适的交互方式通知用户发送失败。

      由于未被确认收到的节可能已经被对方接收,重发有可能导致反复发送。虽然至少能够通过每一个节的id协助接收方排除反复接收的节,可是该协议还无法预防这样的情况的发生。

5. 恢复

    xml流有可能发生意外终止(如,由于网络中断)。这样的情况须要迅速的恢复之前的流,而不是完毕流的新建,花名冊的检索和状态的播送。

    另外。该协议会在先前的连接中交换最后收到的流的序列号,同意实体终于确定哪些节须要重发,哪些节不须要重发,通过重放消除反复。

    请求中流都是可恢复的,当开启流管理时,client必须给<enable/>元素添加一个“resume”属性,它的值能够是“true”或者“1”。

例7:client开启流管理

C: <enable xmlns='urn:xmpp:sm:3' resume='true’/>

    要同意流被恢复,服务端必须在<enable/>元素中包括一个“resume”属性,设为ture或者1;必须包括一个“id”属性,用于作为流的标识符。

例8:服务端同意流恢复

S: <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true’/>

定义:id属性为流管理定义了一个唯一标识(“SM-ID”)。SM-ID必须由服务端生成。client必须认定SM-ID是一个不透明的类型,因此不同意分配给SM-ID不论什么语义。

服务端能够编码不论什么它觉得实用的信息。如full JID<localpart@domain.tld/resource>。赋给SM-ID(如full JID拼上一个当前的属性)。不论什么在XML中被同意的字符属性都是可用的。

SM-ID不同意被当前和以后的会话重用(可是server不须要确保SM-ID一直是唯一的。仅仅有在server继续执行的时候)。SM-ID不应该超过4000个字节。

如上所述,<enable/>元素能够包括一个“location”属性,用于指明重连的首选位置(如。一个为已经联网的client保存状态的特定的链接管理器)。

例9:server会选择在一个特定的位置又一次链接

S: <enabled xmlns='urn:xmpp:sm:3'
id='some-long-sm-id'
location='[2001:41D0:1:A49b::1]:9222'
resume='true’/>

      假设流意外停止了。client就会向server开一个TCP链接。全部事件的顺序例如以下:

1.断开链接后,client向服务端新开一个TCP链接,选择location属性中指明的地址(假设有的话)。

2.client发送初始化的流的header。

3.服务端向client回复流的header。

4.服务端发送流的features。

5.client发送 STARTTLS 请求。

6.服务端通知client继续进行TLS协议。

7.两方完毕一次TLS握手(注意:假设正在进行恢复会话,同一时候也在用TLS,建议利用TLS会话恢复RFC5077,进一步优化XML流的恢复)。

8.client发送新初始化的流的header。

9.服务端向client回复流的header。

10.服务端向client发送流的features,要求SASL协议。提供合适的SASL机制(注意:假设服务端觉得在TLS会话恢复中提供的信息足够的真实可信,它能够提供SASL EXTERNAL 机制;详细的请參考draft-cridland-sasl-tls-sessions [13])。

11.两方完毕SASL协议。

12.client发送新初始化的流的header。

13.服务端向client回复流的header。

14.服务端发送流的features,提供SM的features。

15.client请求恢复之前的流。

注意:这些事件的顺序也可能以上的顺序。取决于服务端提供SM features的时间,client是否选择STARTTLS。等等。另外,其实服务端到服务端的流常常没有完毕SASL协议,甚至也不完毕TLS协议。

上面讲的并没有改变RFC6120中规定的有关流协议的不论什么规则。然后,由于流管理适用于xml节(不是不论什么其它xml元素)的交换,这使得当可能要開始向还有一方发送节的时候(不是之前)。服务端提供SM features变得有意义。

见Recommended Order of Stream Feature Negotiation (XEP-0170) [14]。

    请求恢复之前已经终止的流。client要发送一个<resume/>元素,附上“urn:xmpp:sm:3”的命名空间。<resume/>元素必须包括一个“previd”属性。该属性的值为要恢复的流的SM-ID,还有包括一个h值,该值标识通过要恢复的流。服务端发送给client的最后一个被处理的节的序列号(万一client没有收到不论什么的节。它将设置h值为0)。

例10:流恢复请求

C: <resume xmlns='urn:xmpp:sm:3'
h='some-sequence-number'
previd='some-long-sm-id’/>

    假设server能恢复之前的流,必须返回一个<resumed/>元素。该元素必须包括一个“previd”属性。值为要恢复的流的SM-ID,还有必须包括一个h值。值为该值标识通过要恢复的流,client发送给服务端的最后一个被处理的节的序列号(万一服务端没有收到不论什么的节,它将设置h值为0)。

例11:被恢复的流

S: <resumed xmlns='urn:xmpp:sm:3'
h='another-sequence-number'
previd='some-long-sm-id’/>

    假设服务端不支持会话恢复,必须返回一个<failed/>元素,该元素要包括一个<feature-not-implemented/>的错误条件。假设服务端没有把“previd”识别为一个更早的会话(比如,可能由于之前的会话已经超时),也必须返回一个<failed/>元素,该元素必须包括一个<item-not-found/>的错误条件。在这两种失败的情况中。server都应该同意client在这时绑定一个资源,而不是强制client又一次開始流协议处理过程和又一次认证。

    假设之前的流恢复了,此时服务端还依旧保持着之前认证过(previously-identified)的会话开着,应该终止之前的流。

    当一个会话被恢复时。两方处理步骤例如以下:

    *序列值沿用了之前会话的,并且不会被新的流改动。

    *一旦收到<resume/>或者<resumed/>元素,client和服务端利用h值又一次发送全部由于断开连接而丢失的节。其实。应该处理这俩元素的h值,就像在<a/>元素中处理它一样(即,在发出队列中标记节为已经被处理),除此之外,必须将不论什么仍被标记为未处理的节又一次发送给对方。

    *两方都应该重发在先前的对话中未被标记处理的节,重发时是依据对方记录的序列号。

    *正在重连的client不能请求花名冊,由于在client断开连接的过程中不论什么花名冊信息的改变都会在流管理会话恢复后被发送给client。

    *client不能在其试图恢复之前presence状态的时候发送presence节。由于这个状态可能已经被服务端保存。

    *两方都不能试图重建状态信息(如。Service Discovery (XEP-0030) [15] information)。

6.错误处理

    假设对于一个<enable/>或者<resume/>元素。假设出现了错误。server必须返回一个<failed/>元素。这个元素要包括一个错误条件。它必须是RFC6120里定义的全部节错误条件中的一个。

    以下是个样例。

例12:服务端返回错误

S: <failed xmlns='urn:xmpp:sm:3'>
<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</failed>

    流管理错误被觉得是可恢复的。可是,错误的使用流管理可能会导致流的终止。

7.流的关闭

    一个干净的关闭的流不同于一个未完毕的流。假设client想干净的关闭它的流。而且结束它的会话。它必须发送一个</stream:stream>,以便服务端可以发送一个代表client的unavailable presence信息。

    假设流没有被干净的关闭,那么服务端应觉得流没有完毕(即使client关掉了跟服务端的TCP连接),要在一个有限的时间内保持代表client的会话。

在离开一个未完毕状态的流之前,client能够发送不论什么希望的presence信息。

8.情景(Scenarios)

    以下的几种情景列举了几种不同的流管理的使用。这些样例都是关于client和服务端之间的,可是流管理还能够用于服务端对对服务端的流。

  8.1 主要的acking情景

      流管理协议能够通过acks,在没有恢复会话的能力的条件下提高可靠性。

一个主要的实现将运行下面操作:

      *作为一个client,发送不带不论什么属性的<enable/>。不用管在<enable/>回复中的属性。


未完待续。。。