Presence(http://www.cnblogs.com/jiyuqi/tag/xmpp/)
在XMPP协议中,我们使用presence来获取用户是否已经上线以及是否可以通信的状态。
为了能够知道自己联系人的状态以及让联系人知道自己的状态,用户上线后需要订阅联系人的状态,联系人也同样需要订阅用户的状态。
通过下面的消息订阅联系人的状态:
当联系人接收/拒绝订阅时,会发送消息的消息体(sucribed/unsubscribe)回应。
通常客户端是自动回应这些消息的,当我们订阅了联系人的状态之后,也会受到联系人的状态变更信息。
还可以通过嵌入(chat/away/xa/dnd)和元素表示更加丰富的信息。
需要注意的是presence节是占用带宽最多的节,任何对该节内容的扩展都需要慎重。另外,presence还提供priority属性用来标识资源的优先级(-127-128),负数优先级的资源将无法接收消息,除非显式指定,这个特性通常是由服务器实现的。
另外,我们可以通过发送directed presence到其他用户,来避免订阅对方信息,非常适合应用于网络而上的简短交流。
可以通过发送发送下线通知,服务器会完成消息保存,联系人通知等一系列操作。
presence也有其富文本形式,可以包含更多信息,但不建议在presence中使用(资源和带宽)。Publish-Subscribe[XEP-0060]和Personal Eventing Protocal[XEP-0163]提供了类似功能,但presence是针对整个花名册广播的。
服务器返回的花名册中还可以包含更加丰富的信息,包括用户组以及订阅情况。
用户对花名册的修改也可以通过发送IQ-set(rost-push)同步到服务器及其他客户端上(用户可能有手机/pad等)。这种只推送变更的机制可以简化客户端编程并节省流量。
Instant Messaging
消息发送流程
user1向user2发送消息,user1位于domain1,user2位于domain2
user1的client1向server1发送消息节,server1设置from属性
server1投递消息给server2(直接通信)
server2收到消息并检查user2是否在线并投递
normal
message消息类型
独立消息,将会马上投递或者缓存,是默认消息类型
chat
用户聊天,通过session 建立,通常处理一系列消息
groupchat
多人聊天,通常此类型会指定一个组件或者模块处理多人聊天,该模块会为每个参与者发送消息
headline
全体通知,不会缓存,立即投递
error
错误的信息节,反馈错误信息
延时投递
如果用户不在线,则消息被缓存,用户登录后,消息推送给用户,并且携带消息原始产生时间,用于客户端对消息进行排序。
Chat session
chat session用于用户频繁交流的情况,这类似于现实情况中的聊天,其建立过程为:双方用户在消息交互中知道了对方的Full JID,因此可以直接通信,响应的机制称为chat session。
状态通知类似于QQ的“正在输入”功能,让交互双方了解即时状态。该功能扩展由XEP-0085定义。如果用户不希望对方看到自己的状态,可以选择不响应节。已经定义好的状态有:Starting,Active,Composing,Paused,Inactive,Gone
格式化消息
可以在message消息中加入XHTML用于富文本展示。
协议可以在[XEP-0071]中找到。
vCards
也就是虚拟名片,用户在聊天时可以通过虚拟名片查看相关信息。
阻塞和过滤通信
类似于QQ可黑名单机制,可以对某人隐身,不看某人的消息或屏蔽某个域的消息等等;当然也包括对名单的修改。
更多消息扩展
Extended Stanza Addressing[XEP-0033]
发送一条消息给多个接受者而不通过聊天室
Advanced Message Processing[XEP-0079]
控制消息过期,避免消息被本地存储以及延时投递等
Message Receipt[XEP-00184]
客户端层面确认消息是否已经送达
Message Archiving[XEP-0136]
在服务器上存储消息,而不是在客户端机器上存储
Service Discovery
我们需要知道系统中有哪些实体,以及该实体支持哪些服务,为了完成这些操作,引入了实体发现和服务发现的概念。
Using Service Discovery with Servers and Services
服务发现同样适用iq-get的disco#items/disco#info这两个查询操作,只是将查询是服务而非实体。
具体的查询步骤较为复杂。
Using Service Discovery with Clients
Explicit Service Discovery
这种场景应用于用户的花名册向用户返回是否在线的信息,这种判断是否在线的信息带有Full JID,因此可以通过disco#info/disco#item来查询。
但是这种查询可能返回某个Full JID携带的全部信息,导致数据量过大,因此引入了下面的方式
Entity Capabilites: Service Discovery Shorthand
是对上面方法的改进,通过将实体支持的特性HASH为一组特征吗,客户端接收该特征码后与本地存储进行比较,如果已有该特征码,则可以获得支持的特性列表;如果客户端没有缓冲该特征码,则重新发送disco#info消息获取,并缓存。
采用此种方法可以节省响应的资源。
并且通过presence节就可以获取客户端支持的功能了。
Data Forms
类似于HTML的表单,有工作流的特征,可以实现用户验证码输入和确认等功能。
由[XEP-0004]定义。
Multi-Party InteractionMCU 基础多人聊天最初被称为groupchat,后来的迭代版本改进为Multi-User Chat(UMC)[XEP-0045]。MCU的基本思想是用户加入到一个聊天室,而聊天室会组播消息,聊天室起到消息反射器的作用聊天室有如下特征3.1 消息在所有的参与者中共享3.2 所有的参与者都有一个room roster3.3 参与者都使用其nickname标识,而不是实际的JabberID3.4 房间共享参与信息3.5 参与者不仅限于人,也可以是服务等聊天室有其自己的JID,且该JID是服务器的一个组件,因此具有不同的域,如服务器的域称为:wonderland.lit,组件的域为conference.wonderland.lit,实现MCU需要相应的组件,服务器根据域的不同将消息路由到对应的组件上处理。加入聊天流程5.1 用户发送presence消息5.2 聊天室向成员广播该presence5.3 聊天室向用户发送成员的presence5.4 聊天室向用户发送一些历史消息好让用户参与讨论,消息数目可配置,且消息带有时间戳5.5 之后的聊天消息不再携带时间戳5.6 聊天室之间的消息往来,消息类型为groupchat如果用户向聊天室的成员发送消息,消息类型为chat,但消息实际上使用用户发送给聊天室的,聊天室会改写from/to字段为实际接受者的JID。如果离开聊天室则会发送退席消息
成员管理
群组中有多重角色,不同的角色拥有不同的权限,可以将用户临时踢出,或加入黑名单等。
具体有:outcast,visitor,participant,member,moderator,admin,owner。
另外房间也有不同的类型,有指定名单的,有临时的,有隐藏的,有固定的等。
昵称
用户可以设置其在聊天室内的昵称,参考In-Band Registration[XEP-0077]。
配置
多人聊天可以进行配置,有非常多的可配置项,列出如下。
配置项
作用
allowinvites
是否允许普通成员邀请
changesubject
是否非管理员能够更改聊天室主题
enablelogging
是否开启记录归档
getmemberlist
是否能够获取成员列表
lang
语言
maxuser
最大参与者数量
membersonly
是否仅会员可加入(适用于member类型聊天室)
persistentroom
房间是否为永久(所有成员退出也不会删除)
presencebroadcast
是否广播presence消息,对大room有用
publicroom
该room是否可被发现
roomadmin
设置room管理员
roomdesc
设置room描述
roomname
设置room名称
roomowner
设置room 所有者
whois
控制是否匿名等
数据传输
除了文字之外,MCU还可以传输地理信息,文件和进行远程调用等。
Publish/Subscribe
实际上就是消息系统中的推模式,主要分三个步骤完成:首先订阅一个主题;其次发布一个消息;最后消息被推送到订阅客户端。[XEP-0060]
Subscriptions
订阅者需要订阅一个源,发布者也将消息发布到这个源。
流程:
用户发送订阅请求
服务器响应该订阅请求
如果成功则可以接收消息了,如果失败,则有可能是要求更多的配置信息
订阅者对订阅进行配置(可选)
订阅者请求配置项
源返回订阅配置表单
用户解除订阅
服务器返回解除订阅响应
Publishing and Receiving Notifications
主要分为两个部分。
发布方发布消息到源;服务器将源的消息通过message的形式投递给订阅方。
需要注意的是:发布方只负责将消息推送至源,投递的逻辑由订阅方的服务器完成。
Payloads
用户可以选择是否在通知中携带payloads,如通知中包含头像信息时,只需要metadata,无需具体的数据。
是否启用payload可以通过配置项deliver_payloads来实现。
Items
是否保存items也是可以配置的,可以通过persist_items配置。
保存/不保存数据的node分别称为persistent nodes/transient node。
Discovering Nodes
nodes及其服务可以通过disco#info/disco#item来查询
返回的结果可以通过以上两个命令进一步查询,直到找到想要订阅的node。
Personal Eventing
如果用户想订阅某个好友的动态,可以使用用户的JID作为datanode,相应的简化协议称为PEP[XEP-0163](Personal Eventing Protocal)。
用户可以应用PEP协议,在自己的Presence消息中包含自己的爱好信息,服务器收到该presence后,将会根据此爱好向用户推送好友的动态。
包括User Tune,User Location,User Activity,User Mood,User Nickname,User Avatar等。
Design Decision
尽量不要修改XMPP的核心协议,而是应该试着通过新的namespace去扩展它
由于presence占用了大约90%的数据流量,需要控制presence节中的数据
需要尽量简化XMPP客户端的设计,尤其是需要减少对服务器端的压力
尽量重用已有协议,而不是重新设计一个