您好,欢迎来到爱go旅游网。
搜索
您的当前位置:首页xmpp笔记

xmpp笔记

来源:爱go旅游网


述语:

TLS 传输层安全协议 SASL简单验证和安全层

层的顺序:TCPTLSSASLXMPP

XML节通过XML流来发送,XMPP定义了三种顶级XML节

XMPP给这三种节定义了五种通用属性 to from id type xml:lang

to属性指定接收节的JID。

from属性指定发送者的JID。

id属性是可选的。并且,在接收应用(通常是一个服务器)中是唯一的。注意:流ID可能是严格安全的,并且因此必须是即不能预测也不能重复的

type属性指定目的或消息上下文,出席或IQ节的详细信息。 iq节的type属性有: Type 值 Error Get Result Set 描述 如果一个实体接收了一个类型\"get\"或\"set\"的IQ节包含一个不理解的子元素, 这个实体应该返回一个类型为\"error\"的错误条件的IQ节

presence节的type属性有: Type 值 Available 描述

Subscribe Subscribed Unsubscribe Unsubscribed Unavailable Probe Error Invisible Presence 节的子元素 子元素节点 发送者希望订阅接收者的出席信息 发送者允许接收者接收他们的出席信息 发送者取消订阅另一个实体的出席信息 订阅者的请求被拒绝或以前的订阅被取消 通知实体将不可通信 对一个实体当前的出席信息的请求; 只应由服务器代替一个用户生成 处理或递送之前发送的出席信息节的时候发生了错误 描述 一个特定的实体或资源的特定的可用性状态 一个persence只能有一个show节点 一个可用性状态的自然语言描述 元素包含非可读的XML字符数据指明资源的优先级别. 这个值必须是一个介于-128和+127之间的数字

Show节点的取值:

 away -- 实体或资源临时离开.  chat -- 实体或资源在聊天中是激活的.  dnd -- 实体或资源是忙(dnd = \"不要打扰\").  xa -- 实体或资源是长时间的离开(xa = \"长时间离开\")

如果没有提供元素, 实体被假定是在线和可用的

元素对show的一种描述

message节的type属性有: 类型值 Chat 描述 消息是在一对一聊天会话的语境被发送. 一个兼容的客户端应该在一个允许两个实体进行一对一聊天的界面中显示消息,包括适当的会话历史 发生了一个和上次发送者发送的消息有关的错误(关于节错误语法的详细信息,一个兼容客户端应该在一个适当的界面展示它以通知发送者这个错误的种类 Error

GroupChat 消息是在一个多用户聊天环境的语境下发送的(类似[IRC]). 一个兼容客户端应该在允许多对多聊天的界面显示这个消息,包括, 包括这个聊天室的名册和适当的会话历史. 基于XMPP的群聊协议的完整定义超出了本文的范围 一个消息可能是由一个递送或广播内容的自动化服务生成的(新闻, 体育, 市场信息, RSS feeds, 等等.). 这个消息是不需要回复的, 一个兼容客户端应该在一个适当的和单独消息,聊天会话,或群聊会话不同的界面显示这个消息(例如, 不给接收者提供回复能力) 这个消息是一个在一对一会话或群聊会话之外的单独消息, 并且它希望接收者能够回复.一个兼容客户端应该在一个允许接收者回复的界面显示这个消息, 但不需要会话历史 元素描述 主题 主体 线索 Headline Normal Message 子元素 元素节点

名册管理

一个名册项相的presence信息订阅状态从元素的”subscription”可以得到。 述语:这个用户---用A来表示 这个联系人---用B来表示

Subscription属性值 值 none 值描述 这个用户(A)没有对这个(item行中的)联系人(B)出席信息的订阅, 这个联系人(B)也没有订阅用户(A)的presence信息,即A,B没有任何订阅关系 这个用户订阅了这个联系人的presence信息, 但是这个联系人没有订阅用户的presence信息。即B是A的好友,但A不是B的好友 这个联系人订阅了用户的presence信息, 但是这个用户没有订阅这个联系人的presence信息。即A是B的好友,但B不是A的好友。 to from

both 用户和联系人互相订阅了对方的出席信息 A,B互为好友 每个条目可以包含一个'name'属性, 它设置和这个JID相关的\"nickname\取决于用户(而不是联系人). 'name'属性的值是不透明的.

每个条目可以包含一个或多个子元素,用于把名册条目收集到多个类别之中. 子元素的XML字符数据是不透明的.

xml:lang属性值指定任意可读XML字符数据的缺省语言

节定义了消息语义,节可被看作“推”机制,一个实体推信息给其它实体,与EMAIL系统中发生的通信类似。所有消息节应该拥有‘to’属性,指定有意的消息接收者;根据接收到那样的一个节,服务器应该路由或传送它到有意的接收者。

节定义了出席语义,节可被看作基本广播或“出版-订阅”机制,多实体收到他们已订阅(在这种情况下,网络可利用信息)实体的信息。总的来说,出版实体应该发送一个不带‘to’属性的出席节,在这种情况下,与此实体相连的服务器应该广播给所有订阅实体。然而,一个出版实体也可能发送一个带有‘to’属性的出席节,此种情况下,服务器应该路由或传送节到有意的接收者。

节定义了请求语义,节可被看作一个请求-响应机制,与[HTTP]在某些方面相似。IQ语义让一个实体向其它实体请求或接收其它实体的响应成为可能。请求与响应的数据内容由IQ无素的直接子元素的命名空间声明定义,并且,交互由请求实体通过使用‘id’属性来跟踪。因此,IQ交互遵从结构化数据交换的一个通用模式,此交换例如得到/结果或设置/结果(虽然如果合适的话,对一个请求的响应可能会以错误返回)。

订阅状态

从用户的角度,已定义的发种可能的订阅状态。 状态 None None + Pending Out 描述 联系人和用户互相没有被对方订阅, 并且也都没有从对方那里请求一个订阅 联系人和用户互相没有被对方订阅, 用户已经向联系人发送了一个订阅请求但还没有收到回复 联系人和用户互相没有被对方订阅, 联系人已经向用户发送了一个订阅请求但还没有收到回复(注意: 在这种状态下联系人的服务器None + Pending In

不应该推送或递送名册条目, 但是应该等待,直到联系人的订阅请求已经从用户那里得到批准) None + Pending Out/In 联系人和用户互相没有被对方订阅, 联系人已经向用户发送了一个订阅请求但还没有收到回复, 用户已经向联系人发送了一个订阅请求但还没有收到回复 用户已订阅联系人(单向) 用户已订阅联系人, 联系人已经向用户发送了一个订阅请求但还没有收到回复 联系人已订阅用户(单向) 联系人已订阅用户(单向), 用户已经向联系人发送了一个订阅请求但还没有收到回复 用户和联系人互相被对方订阅了(双向) To To + Pending In From From + Pending Out Both

对上述9种状态,用户进行subscribed(授予订阅权限)节处理后,用户的状态变化 以下描述里A为用户,B为联系人

出站:用户即收到服务器发来的消息用户做出响应后向服务器发出的动作。 入站:用户服务器收到联系人发来的消息后,转后给用户的所有资源的动作。

消息发出方(出站) 当前状态 None None+Pending Out 是否路由发过来? 否 否 新状态 状态不变 状态不变 描述 A,B无订阅关系,故状态不变 A当前为发出订阅请求,但B还没有响应,故状态不变 B向A发送一个订阅请求,这时A进行subscribed处理之后,状态变为From(即A同为成为B的好友) 收到B发来订部A的请求,A进行subscribed处理后,A将成为B的好友,但A发出的订阅请求,B还没有作回应 B已是A的好友,没有消息请求,故状态不变 在B已是A好友的情况下,B向A发来订阅A的请求,当A进None+Pending In 是 From None+Pending Out/In 是 Form+Pending Out To 否 状态不变 To+Pending In 是 Both

行subscribed处理后,A也同意成为B的好友。故状态变为both From 否 状态不变 A已是B的好友,没有由到路由发来的请求,故不变 在A已是B好友的情况下,向B发了订阅请求,但B没有应答,所以也没有路由消息要处理,故不变 From+Pending Out 否 状态不变 Both 否 状态不变

(出站)用户作拒绝处理(unsubscribed) 当前状态 None None+Pending Out 是否路由发过来? 否 否 新状态 状态不变 状态不变 描述 A,B无订阅关系,故状态不变 A当前为发出订阅请求,但B还没有响应,故状态不变 B发来订阅A的请求,A作拒绝处理后状态变为None A发出的请求未得到B的请求,这时收到B发来的订阅A的请求,A进行拒绝(unsubscribed)处理后,状态变为None+Pending Out B已同意成为A的好友 在B已是A的好友的情况下,收到B发来的订阅请求,A拒绝后,状态变为To A已经同意成为B的情况下,A选择取消授权(unsubscribed)之后,A,B之间没有任何关系,即变为None A成为B的好友情况下,A发出订阅请求None+Pending In 是 None None+Pending Out/In 是 None+Pending Out To To+Pending In 否 是 状态不变 To From 是 None From+Pending Out 是 None+Pending Out

给B后,进行拒绝 Both 是 To 在,AB互为好友的情况,A取消授权给B,哪么就回到了单方好友,即A将不再成为B的好友,但B还是A的好友。 用户收到presence订阅消息(入站)时服务器处理 A:用户 B:联系人

入站subscribe节处理 当前状态 None 是否路由发过来? 是 新状态 None+Pending in 描述 当B发送订阅(subscribe)请求给A,A服务器收到后,状态变为None+Pending In 此时A的状态为发送订阅B的请求但未得到回应,这个时侯A的服务器收到B发来的subscribe请求,所以状态变为None+Pending Out/In B发来订阅A未作回应,故不变 B发来订阅A未作回应,故不变 此时状态为B是A的好友,B发来订阅请求给A的服务器,服务器将状态改为to+pending In A在此状态下服务器没有接收subscribe消息,故不变 A在此状态下服务器没有接收subscribe消息,故不变 A在此状态下服务器没有接收subscribe消息,故不变 A在此状态下服务器None+Pending Out 是 None+Pending Out/In None+Pending In None+Pending Out/In To 否 否 是 状态不变 状态不变 To+Pending In To+Pending In 否 状态不变 From 否 状态不变 From+Pending Out 否 状态不变 Both 否 状态不变

没有接收subscribe消息,故不变 处理入站unsubscribe A:用户 B:联系人 当前状态 None 是否路由发过来? 否 新状态 状态不变 描述 A,B没有任何关系,所以A服务器收到unsubscribe时,状态不需要改变。 A,B没有任何关系,所以A服务器收到unsubscribe时,状态不需要改变。 B发出订阅消息给A,在A未作回应的时候,B又发出了unsubscribe进行取消,A的服务器收到后,将状态改为None A在None+Pending Out/In状态下,B发送unsubscribe进行取消后,A的服务器将状态改为None+Pending Out B已是A的好友,但B没有订阅过A,所以B进行取消订阅,没有改变状态。 B目前已是A的好友,并且B发送订阅请求给A,在A未作回应情况下,B发出了unsubscribe来取消,所以状态又变回了To A已是B的好友,现在B不想再订阅A了,发出unsubscribe进行取消后,状态改为None A已是B的好友,现None+Pending Out 否 状态不变 None+Pending In 是 None None+Pending Out/In 是 None+Pending Out To 否 状态不变 To+Pending In 是 To From 是 None From+Pending Out 是 None+Pending Out

在B不想再订阅A了,发出unsubscribe进行取消后,状态改为None+Pending Out Both 是 To 双方为好友的情况下,B取消对A的订阅,即解决关系,这里回到了,B是A的好友,但A不再是B的好友了。 处理入站subscribed(订阅授权) A:用户 B:联系人 当前状态 None None+Pending Out 是否路由发过来? 否 是 新状态 状态不变 To 描述 A,B没有关系,所以状态不变 A此时已经发送订阅给B,现在B进行同意后,A的服务器将变为To A没有发任何订阅B的请求,所以B发授权状态也不会变 A在None+Pending Out状态下,收到subscribed后变为to所以这里的状态为To+Pending In A没有发任何订阅请求,所以不变 A没有发任何订阅请求,所以不变 A没有发任何订阅请求,所以不变 此时A已是B的好友,在A发出订阅请求后,B同意(subscribed)之后就变为Both A没有发任何订阅请求,所以不变 None+Pending In 否 状态不变 None+Pending Out/In 是 To+Pending In To To+Pending In From From+Pending Out 否 否 否 是 状态不变 状态不变 状态不变 Both Both 否 状态不变

入站处理 unsubscribed A:用户 B:联系人 当前状态 None None+Pending Out None+Pending In None+Pending Out/In To 是否路由发过来? 否 是 否 是 是 新状态 状态不变 None 状态不变 None+Pending In None 描述 遇到B的拒绝,所以变为None 遇到B的拒绝,所以变为None A订阅B,B同意后状态变为To,但B想取消同意的订阅,所以又变回了None A订阅B,B同意后状态变为To,但B想取消同意的订阅,所以又变回了None A没有发出订阅请求,所以不变 A发出请求被B拒绝,又变回了From B解除了A的订阅,但A没有解解B的订阅,成单方好友。 To+Pending In 是 None+Pending In From From+Pending Out Both 否 是 是 状态不变 From From

订阅状态变更通知 节类型 Subscribe Subscribed Unsubscribe Unsubscribed

客户端:(支持多终端)

多个终端登录,使用resource 如:

Example@fsh.com/PC example@fsh.com/Iphone example@fsh.com/android

红色的地方为资源。

接受 Subscribed Subscribe Unsubscribed Unsubscribe 禁止 Unsubscribed unsubscribe Subscribed subscribe

JID

合法的JID应包括:域名(domain),节点名(node),资源名(resource) 且长度不能超过1023字节。

jid = [ node \"@\" ] domain [ \"/\" resource ] domain = fqdn / address-literal

fqdn = (sub-domain 1*(\".\" sub-domain))

sub-domain = (internationalized domain label) address-literal = IPv4address / IPv6address

stream 属性

to from id 初始化方发给接收方 接收方发给初始化方 接收方的主机名 忽略 忽略 忽略 接收方的主机名 会话键值 缺省语言 支持XMPP 1.0 xml:lang 缺省语言 version 支持XMPP 1.0

流错误的条件。 条件标签 条件描述 实体已经发送XML但是不能被处理;这个错误可以(可以)被更多特定的XML相关的错误替换,比如 , , , , 以及 ,尽管更多特定的错误是首选的。 实体发送的名字空间前缀不被支持,或者在一个需要某种前缀的元素中没有发送一个名字空间前缀 服务器正在关闭为这个实体激活的流,因为一个和已经存在的流有冲突的新的流已经被初始化。 实体已经很长时间没有通过这个流发生任何通信流量(可由一个本地服务策略来配置). 初始化实体在流的头信息中提供的'to'属性的值所指定的主机已经不再由这台服务器提供 由初始化实体在流的头信息中提供的 'to' 属性的值和由服务器提供的主机名不一致

一个在两台服务器之间传送的节缺少 'to' 或 'from' 属性(或者这个属性没有值). 服务器配置错误或者其他未定义的内部错误,使得服务器无法提供流服务 在'from'属性中提供的 JID 或 主机名地址,和认证的 JID不匹配 或服务器之间无法通过SASL(或回拨)协商出合法的域名,或客户端和服务器之间无法通过它进行认证和资源绑定。 流 ID 或回拨 ID 是非法的或和以前提供的 ID 不一致. 流名字空间和 \"http://etherx.jabber.org/streams\" 不相同或回拨名字空间和 \"jabber:server:dialback\" 不相同 实体通过流发送了一个非法的XML给执行验证的服务器 实体试图在流被验证之前发送数据或不被许可执行一个和流协商有关的动作,接收实体在发送错误信息之前不允许处理的节 实体违反了某些本地服务策略;服务器可以选择在 元素或应用程序定义的错误条件(元素)中详细说明策略 服务器无法正确连接到用于验证或授权的远程实体 服务器缺乏必要的系统资源为流服务 实体试图发送受限的XML特性,比如一个注释,处理指示,DTD,实体参考,或保留的字符 服务器将不提供服务给初始化实体但是把它重定向到另一台主机;服务器应该在元素的XML字符数据中指明替代服务器名或IP地址(它必须是合法的域名标识) 服务器正在关机并且所有激活的流正在被关闭 错误条件不在本文已定义的错误条件列表之中;这个错误条件应该仅用于\"应用程序定义条件\"元素 初始化实体以一个服务器不不支持的编码方式编码了一个流 初始化实体发送了一个流的一级子元素但是服务器不支持 由初始化实体在流的头信息中指定的

'version'属性的值所指定的版本不被服务器支持;服务器可以在元素中指定一个它支持的版本号

SASL错误条件定义 条件节点 条件描述 接收实体认可由初始化实体发送的元素;在回应一个元素时发送 由初始化实体提供的数据无法处理,因为[BASE64]编码不正确; 在回应一个包含初始化响应数据的 元素或元素时发送 由初始化实体提供的授权id是非法的,因为它的格式不正确或初始化实体无权给那个ID授权;在回应一个包含初始化响应数据的 元素或元素时发送 初始化实体不能提供一个机制活、或请求一个不被接受实体支持的机制;在回应一个元素时发送 初始化实体请求的机制比服务器策略对它的要求弱;在回应一个包含初始化响应数据的 元素或元素时发送 验证失败,因为初始化实体没有提供合法的credentials(这包括但不限于未知用户名等情形);在回应一个包含初始化响应数据的 元素或元素时发送 验证失败,因为接收实体出现了临时的错误;在回应一个 元素或元素时发送 初始化实体发送了一个不规范的XML

TLS握手过程 客户端-----服务端

步骤 1: 客户端初始化流给服务器

步骤 2: 服务器发送一个流标签给客户端作为应答

步骤 3: 服务器发送 STARTTLS 范围给客户端(包括验证机制和任何其他流特性)

DIGEST-MD5 PLAIN

步骤 4: 客户端发送 STARTTLS 命令给服务器

步骤 5: 服务器通知客户端可以继续进行

步骤 5 (或者): 服务器通知客户端 TLS 握手失败并关闭流和TCP连接

步骤 6: 客户端和服务器尝试通过已有的TCP连接完成 TLS 握手. 步骤 7: 如果 TLS 握手成功, 客户端初始化一个新的流给服务器

步骤 7 (或者): 如果 TLS 握手不成功, 服务器关闭 TCP 连接.

步骤 8: 服务器发送一个流头信息应答客户端,其中包括任何可用的流特性 xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='c2s_234' version='1.0'>

DIGEST-MD5 PLAIN EXTERNAL

服务端------服务端

步骤 1: Server1 初始化流给 Server2 to='example.com' version='1.0'>

步骤 2: Server2 发送一个流标签给 Server1 作为应答

步骤 3: Server2 发送 STARTTLS 范围给 Server1 ,包括验证机制和任何其他流特性

DIGEST-MD5 KERBEROS_V4

步骤 4: Server1 发送 STARTTLS 命令给 Server2

步骤 5: Server2 通知 Server1 允许继续进行

步骤 5 (或者): Server2 通知 Server1 TLS握手失败并关闭流

步骤 6: Server1 和 Server2 尝试通过 TCP 完成 TLS 握手.

步骤 7: 如果 TLS 握手成功, Server1 初始化一个新的流给 Server2

步骤 7 (或者): 如果 TLS 握手不成功, Server2 关闭 TCP 连接. 步骤 8: Server2 发送一个包含任何可用流特性的流头信息给 Server1 xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='s2s_234' version='1.0'>

DIGEST-MD5 KERBEROS_V4 EXTERNAL

SASL握手过程

客户端-----服务端

步骤 1: 客户端初始化流给服务器

步骤 2: 服务器向客户端发送流标签作为应答

步骤 3: 服务器通知客户端可用的验证机制

DIGEST-MD5 PLAIN

步骤 4: 客户端选择一个验证机制

步骤 5: 服务器发送一个 [BASE64] 编码的挑战给客户端

cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgi LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==

解码后的挑战信息是

realm=\"somerealm\ qop=\"auth\

步骤 5 (替代): 服务器返回一个错误给客户端

步骤 6: 客户端发送一个[BASE64]编码的回应这个挑战

dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo YXJzZXQ9dXRmLTgK

解码后的回应信息是

username=\"somenode\

nonce=\"OA6MG9tEQGm2hh\ nc=00000001,qop=auth,digest-uri=\"xmpp/example.com\ response=d388dad90d4bbd760a152321f2143af7,charset=utf-8

步骤 7: 服务器发送另一个[BASE64]编码的挑战给客户端

cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=

解码后的挑战信息是

rspauth=ea40f60335c427b5527b84dbabcdfffd

步骤 7 (或者): 服务器返回一个错误给客户端

步骤 8: 客户端应答这个挑战

步骤 9: 服务器通知客户端验证成功

步骤 9 (或者): 服务器通知客户端验证失败

步骤 10: 客户端发起一个新的流给服务器

步骤 11: 服务器发送一个流头信息回应客户端,并附上任何可用的特性(或空的features元素):

xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345' from='example.com' version='1.0'>

服务端-------服务端

步骤 1: 服务器1 发起一个流给 服务器2

步骤 2: 服务器2 回应一个流标签给 服务器1

步骤 3: 服务器2 通知 服务器1 可用的验证机制

DIGEST-MD5 KERBEROS_V4

步骤 4: 服务器1 选择一个验证机制

步骤 5: 服务器2 发送一个[BASE64]编码的挑战给 服务器1

cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9 ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz

解码后的挑战信息是

realm=\"somerealm\ qop=\"auth\

步骤 5 (替代): 服务器2 返回一个错误给 服务器1

步骤 6: 服务器1 发送一个[BASE64]编码的回应这个挑战

dXNlcm5hbWU9ImV4YW1wbGUub3JnIixyZWFsbT0ic29tZXJlYWxtIixub25j ZT0iT0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5j PTAwMDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5v cmciLHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3 LGNoYXJzZXQ9dXRmLTgK

解码后的应答信息是

username=\"example.org\

nonce=\"OA6MG9tEQGm2hh\ nc=00000001,qop=auth,digest-uri=\"xmpp/example.org\ response=d388dad90d4bbd760a152321f2143af7,charset=utf-8

步骤 7: 服务器2 发送另外一个[BASE64]编码的挑战给 服务器1

cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=

解码后的挑战信息是

rspauth=ea40f60335c427b5527b84dbabcdfffd

步骤 7 (或者): 服务器2 返回一个错误给 服务器1

步骤 8: 服务器1 回应挑战

步骤 8 (或者): 服务器1 中止协商

步骤 9: 服务器2 通知 服务器1 验证成功

步骤 9 (或者): 服务器2 通知 服务器1 验证失败

步骤 10: 服务器1 重新发起一个新的流给 服务器2

步骤 11: 服务器2 发送一个流头信息应答 服务器1 ,并附上任何可用的特性(或一个空的features元素)

xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='s2s_345' version='1.0'>

服务器回拨

发起服务器 接收服务器 ----------- --------- | | | 建立连接 | | ----------------------> | | | | 发stream头 | | ----------------------> | | | | 发stream头 | | <---------------------- |

| |

| 发回拨key | 授权服务器 | ----------------------> | ------------- | | | | 建立连接 | | ----------------------> | | | | 发stream头 | | ----------------------> | | | | 发stream头 | | <---------------------- | | |

| 发验证请求 | | ----------------------> | | | | 发验证响应 | | <---------------------- | | | 报告回拨结果 | | <---------------------- | | |

服务器之间互动的细节协议如下:

1. 发起服务器和接受服务器建立TCP连接. 2. 发起服务器发送流头信息给接收服务器

3. 接收服务器应该(SHOULD)回送一个流头信息给发起服务器,为这次交互生成一个唯一性的ID

4. 发起服务器发送一个回拨密钥给接收服务器

98AF014EDC0...

5. 接收服务器向发起服务器声明的那个域建立一个 TCP 连接,作为结果它连接到授权服务器. (注意: 为了优化性能, 在这里一个实现可以(MAY)重用现有的连接.) 6. 接收服务器发送一个流头信息给授权服务器

7. 授权服务器发送流头信息给接收服务器

8. 接收服务器发送一个密钥检查请求给授权服务器

98AF014EDC0...

9. 授权服务器检查密钥是否合法

10. 接收服务器通知发起服务器结果

会话的建立

1. 流验证 -- 客户端在尝试建立一个会话或发送任何XML节之前必须完成

2. 资源绑定 -- 完成流验证之后, 一个客户端必须绑定一个资源到流上,使得客户端的地址符合格式

服务器向客户端声明会话确定特性 xmlns='jabber:client'

xmlns:stream='http://etherx.jabber.org/streams' id='c2s_345'

from='example.com' version='1.0'>

收到需要会话确立的通知之后(并且是在完成资源绑定之后), 客户端如果想使用即时消息和出席信息功能必须建立一个会话; 它向服务器发送一个符合'urn:ietf:params:xml:ns:xmpp-session'名字空间的类型为\"set\"并包含空的子元素的IQ节以完成这一步骤

步骤 1: 客户端向服务器请求会话

步骤 2: 服务器通知客户端会话已经建立

建立会话之后, 一个 已连接的资源就被称为一个 激活的资源\"active resource\".

许多错误条件是可能的. 例如, 服务器可能遭遇一个内部条件阻碍了它建立会话, 用户名或授权身份可能缺乏建立会话的许可, 或同一个名字相关的这个资源ID已经有一个激活的资源.

如果服务器遭到一个内部条件阻碍了它建立会话, 它必须返回一个错误

步骤2返回的错误情况。

情况一、服务器应答一个错误(内部服务器错误)

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

情况二、服务器应答错误(用户名或资源不被允许建立一个会话)

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

情况三、服务器通知现有的激活的资源 资源冲突(1)

冲突(2)

信息交流(message)

to='romeo@example.net/orchard'

from='juliet@example.com/balcony' type='chat' xml:lang='en'> I implore you! xml:lang='cz'>Úpěnlivě prosim! Art thou not Romeo, and a Montague?

e0ffe42b28561960c6b12b944a092794b9683a38 to='juliet@example.com/balcony' from='romeo@example.net/orchard' type='chat' xml:lang='en'>

Neither, fair saint, if either thee dislike. e0ffe42b28561960c6b12b944a092794b9683a38 to='romeo@example.net/orchard' from='juliet@example.com/balcony' type='chat' xml:lang='en'>

How cam'st thou hither, tell me, and wherefore? e0ffe42b28561960c6b12b944a092794b9683a38 Eg: A的JID: a@example.com/PC

B的JID: b@example.com/PC

当A没有主动发信息到B的情况下B要发信息到A,哪么在message节中的to属性将使用纯JID。如果A发过一次信息给B并且会话还没有断开的情况下。B回复A,这个时候to属性使用的必须为全JID

呈现信息交流(presence)

初始化呈现信息

dnd

Wooing Juliet

Ja dvořím Juliet 1

个人信息发布例子

A用户:A@example.net, 他有一个可用的资源, 资源ID为 \"orchard\"

A服务器中联系人(花名册)列表中存在的用户:

B@example.com (subscription=\"both\" 并且她有两个可用的资源, 一个资源名为\"PC\" 而另一个资源名为 \"IPHONE\")

C@example.org (subscription=\"to\") D@example.org (subscription=\"from\")

步骤 1: 用户A发送初始化presence信息:

步骤2: 用户A的服务器代替用户A发送presence信息调查(探索)给 subscription=\"to\" 和 subscription=\"both\" 的联系人的可用资源: type='probe'

from='A@example.net/orchard' to='B@example.com'/> type='probe'

from='A@example.net/orchard' to='C@example.org'/>

步骤3: 用户A的服务器代替用户A发送初始化presence信息给 subscription=\"from\" 和 subscription=\"both\"的联系人的可用资源: from='A@example.net/orchard' to='B@example.com'/> from='A@example.net/orchard' to='D@example.org'/>

步骤4: 联系人的服务器代替所有可用的资源应答presence信息调查: from='B@example.com/IPHONE' to='A@example.net/orchard' xml:lang='en'> away

be right back 0

from='B@example.com/PC'

to='A@example.net/orchard'> 1 from='C@example.org/pda' to='A@example.net/orchard' xml:lang='en'> dnd

gallivanting

步骤5: 联系人的服务器递送用户的初始化presence信息给所有可用的资源或返回错误给用户:

from='A@example.net/orchard' to='B@example.com/PC'/> from='A@example.net/orchard' to='B@example.com/IPHONE'/> type='error'

from='D@example.org'

to='A@example.net/orchard'>

发送presence给不在花名册中的用户

用户直接发送presence信息给另一个不在他的名册中的用户: from='A@example.net/orchard' to='nurse@example.com' xml:lang='en'> dnd

courting Juliet 0

更新presence

用户发送更新的可用出席presence用于广播:

away

I shall return! 1

用户A的服务器仅向一个联系人广播更新的presence信息 (不是那些返回错误的联系人,也不是那些用户直接向其发送presence信息的联系人): from='A@example.net/orchard' to='B@example.com' xml:lang='en'> away

I shall return! 1

联系人的服务器递送更新的presence信息给联系人所有可用的资源: [to \"IPHONE\" resource...] from='A@example.net/orchard' to='B@example.com' xml:lang='en'> away

I shall return! 1

[to \"PC\" resource...] from='A@example.net/orchard' to='B@example.com' xml:lang='en'> away

I shall return! 1

联系人的资源之一广播最后presence信息:

联系人的服务器发送不可用的presence信息给用户: type='unavailable'

from='juliet@example.com/balcony' to='romeo@example.net/orchard'/> 用户发送最后presence信息:

gone home

用户的服务器广播不可用的presence信息给联系人,包括用户直接向其发送presence信息的那个人: type='unavailable'

from='romeo@example.net/orchard' to='juliet@example.com' xml:lang='en'>

gone home from='romeo@example.net/orchard' to='nurse@example.com' xml:lang='en'>

gone home

管理订阅

A发送一个订阅请求给B。(A请求订阅B的presence) A取消订阅B的请求。

B同意订阅请求:

B不同意订阅请求:

如果B已经同意了A的订阅请求,现在B想取消曾允许的订阅请求

Roster 获取联系人列表

在即时聊天 (IM) 应用中,客户端登录服务器后做的第一个操作通常是获取联系人列表。获取联系人列表需要发送 get 类型的 Iq 数据包。

客户端:

该请求的意义为:名为 juliet 的用户 (登录资源为 balcony) 向 example.com 服务器请求获得 (get) roster 表。

服务器收到请求后,返回 roster 表。 服务端:

subscription='both'> Friends

subscription='from'> Friends

subscription='both'> Friends

可以看到,juliet 的 roster 表内有3个联系人,分别名为 Romeo,Mercutio,Benvolio,都属于 Friends 分组。Roster 列表中的 JID 信息将会用在稍候客户端发送信息包的目的地址中。

Item 中的 subscription 关系到联系人状态信息的传输,有 none,both,from,to 四种。

添加联系人到好友列表(花名册)

Servants

添加成功后,服务器将会下发成功后的列表给各个资源终端进行同步。 id='a78b4q6ha463'>

subscription='none'> Servants

id='a78b4q6ha464'>

subscription='none'> Servants

当各终端接到名单添加成功要求同步的下发列表后,再行应答服务器。 id='a78b4q6ha463'/>

id='a78b4q6ha464'/>

更新联系人

subscription='both'> Friends Lovers

没发现跟添加联系人有哪些不同之处?

删除联系人

不管是添加,修改,删除联系人,服务器都要通知(同一JID,不同资源)各个终端进行同步。

订阅联系人(A订阅B)

jid='contact@example.org' name='MyContact'>

MyBuddies

当A的服务器收到A的订阅联系人的请求,(服务器将向A的所有资源[可能是iphone,PC登录的]进行下发)

jid='contact@example.org' subscription='none' name='MyContact'>

MyBuddies

请求联系人的presence信息的订阅(A订阅B的presence信息)

A的服务器将向A的其它资源广播:

jid='contact@example.org' subscription='none' ask='subscribe' name='MyContact'>

MyBuddies

屏蔽通信

语法:

type='[jid|group|subscription]' value='bar'

action='[allow|deny]' order='unsignedInt'> []

[] [] []

说明:如果type为Jid的时候,value的属性值必须为一个合法的JID,此JID应该满足如下

顺序:

1. (仅为匹配的资源) 2. (任何匹配的资源) 3. (仅匹配的资源)

4. (匹配这个域本身, 正如任何 user@domain, domain/resource, 或 包含一个子域的地址

如果type为group的时候,此时value的属性值为用户花名册(好友列表)中的一个JID,如果这个JID不存在,此时服务器应该返回给客户端一个节错误。

如果type为”subscription”时,value的属性值为:”both”,”to”,”from”,”none”

如果没有带type属性,哪么这个规则将返回”fall-through”错误。

Action 属性值只能是”allow”允许或”deny”禁止

Order属性值必须是唯一并且大于0的整数,如果不唯一的值建立或更新时,服务器将返回节错误。

元素,包括一个或多个子元素,这些子元素指明为哪第屏蔽的种类。 -- 屏蔽引入消息节 -- 屏蔽引入 IQ 节

-- 屏蔽引入出席信息通知 -- 屏蔽外出出席信息通知

在\"jabber:iq:privacy'名字空间之内, 一个\"set\"类型的IQ节的子元素不能包含超过一个子元素(换言之, 这个节必须只包含一个元素, 一个元素, 或一个元素),如果违反了这个规则,服务器将返回一个节错误。

当一个客户端增加或更新一个隐私列表, 元素应该包含至少一个子元素; 当一个客户端移除一个隐私列表的时候, 元素不能包含任何子元素

例子说明:

查找服务器中存在的隐私列表的名字 客户端向服务器发送查询请求消息

服务器发送隐私列表给客户端。

当查询到隐私列表后,逐个取列表的例子: 取public隐私列表

服务器下发public列表给客户端

value='tybalt@example.com' action='deny' order='1'/>

请求private聊私列表

服务器下发private隐私列表

action='allow' order='10'/>

请求special隐私列表

服务器下发special列表

value='juliet@example.com' action='allow' order='6'/> value='benvolio@example.org' action='allow' order='7'/> value='mercutio@example.org' action='allow' order='42'/>

上述说明:

(1) 'public', 它允许所有人的通信,除了一个指定的实体(这是缺省列表); (2) 'private', 它只允许和这个用户有双向订阅的联系人的通信(这是激活的列表); 还有 (3) 'special', 它只允许三个指定的实体通信

如果客户端请求一个列表不存在名字,服务器必须返回错误

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

用户一次只能接收一个列表,如果请求中带多个列表,将会收到服务器发来的节的错误

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

管理激活列表

客户端请求激活列表变更

如果激活成功服务器将下发

如果要激活的名字不存在将下发错误

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

客户取消使用激活列表

服务器下发取消成功

变更缺省列表

服务器下发成功消息。

如果请求变更的列表正在被其它客户端资源占用哪么将下发冲突。

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

取消使用缺省列表

服务器下发成功取消消息。

修改隐私列表

value='tybalt@example.com' action='deny' order='3'/> value='paris@example.org' action='deny' order='5'/>

如果修改成功,服务器下发

当修改成功后,服务器同时推送给该客户端的正在连接的各个资源。比如:A@fsh.com/PC进行修改聊私列表,同时也有一个A@fsh.com/IPHONE和A@fsh.com/Android 哪么服务器将推送修改消息到另外的资源。 推送消息

各资源收到服务端发来的消息后必须做出响应

添加一个新的隐私列表,如果名字和服务器的相同,将复盖掉服务器上的,这跟修改相同。 注意新增或修改是都必须有ITEM节,删除的时候不带ITEM节

删除一个隐私列表

成功移除后消息

屏蔽消息

基于JID的用户屏蔽

value='tybalt@example.com' action='deny' order='3'>

屏蔽后,用户将不会接收到从特定JID发来的消息

基于花名册的用户屏蔽

基于订阅状态的用户屏蔽

全局的用户屏蔽

屏蔽进来的presence信息通知

基于JID的presence入站屏蔽

value='tybalt@example.com' action='deny' order='7'>

基于名册组的用户屏蔽presence信息进入

基于订阅状态的用户屏蔽

全局的用户屏蔽

屏蔽发出的presence信息通知

对于JID的

value='tybalt@example.com' action='deny' order='13'>

对于Group的

对于订阅状态

对于全局的

对于IQ节的屏蔽也一样,有基于JID,基于GROUP,基于订阅,基于全局。 value='tybalt@example.com' action='deny' order='29'>

其它与前面的类似,这里不再详写。

屏蔽所有通信 基于JID

value='tybalt@example.com' action='deny' order='23'/>

基于组

基于订阅

基于全局用户屏蔽

已被屏蔽的实体尝试和用户通信

已被屏蔽的实体尝试发送 IQ get to='romeo@example.net'

from='tybalt@example.com/pda' id='probing1'>

服务器返回一个错误给已被屏蔽的实体 from='romeo@example.net' to='tybalt@example.com/pda' id='probing1'>

xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>

高级启发

当建立一个高级隐私启发的表达式的时候, 客户端应该(SHOULD)使用尽可能简单的表达式.

例如, 启发 \"屏蔽不在我名册中的任何用户的通信\" 可以使用以下任何一种方式来构造:

 允许任何来自我的名册中的JID的通信 (换言之, 列出每个JID成为单独的列

表条目), 但是屏蔽和其他任何人的通信

 允许任何来自我的名册的某个组中的用户的通信(换言之, 列出每个组作为单

独的条目), 但是屏蔽和任何其他人的通信

 允许任何我的他(她)之间的订阅状态为'both'或'to'或'from'的用户的通信

(换言之, 单独列出每个订阅状态值), 但是屏蔽和任何其他人的通信

 屏蔽和任何订阅状态为'none'的用户的通信

最后一个表达式是最简单的并且应该(SHOULD)被使用; 这种情形下将被发送的XML如下:

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- igat.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务