基于XMPP的即时通信系统的建立(四)— 协议详解
Presence
在XMPP协议中,我们使用presence来获取用户是否已经上线以及是否可以通信的状态。
为了能够知道自己联系人的状态以及让联系人知道自己的状态,用户上线后需要订阅联系人的状态,联系人也同样需要订阅用户的状态。
通过下面的消息订阅联系人的状态:
<presence from="alice@wonderland.lit" to="sister@realworld.lit" type="subscribe"/>
当联系人接收/拒绝订阅时,会发送消息的消息体(sucribed/unsubscribe)回应。
通常客户端是自动回应这些消息的,当我们订阅了联系人的状态之后,也会受到联系人的状态变更信息。
还可以通过嵌入<show/>(chat/away/xa/dnd)和<status/>元素表示更加丰富的信息。
<presence>
<show>away</show>
<status>Having a spot of tea</status>
</presence>
需要注意的是presence节是占用带宽最多的节,任何对该节内容的扩展都需要慎重。
另外,presence还提供priority属性用来标识资源的优先级(-127-128),负数优先级的资源将无法接收消息,除非显式指定,这个特性通常是由服务器实现的。
另外,我们可以通过发送directed presence到其他用户,来避免订阅对方信息,非常适合应用于网络而上的简短交流。
可以通过发送<presence type=”unavailable” />发送下线通知,服务器会完成消息保存,联系人通知等一系列操作。
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
错误信息节,反馈错误信息
延时投递
如果用户不在线,则消息被缓存,用户登录后,消息推送给用户,并携带消息原始产生时间,用于客户端对消息进行排序。
可以参考Delayed Delivery[XEP-0203]
Chat session
chat session用于用户频繁交流的情况,这类似于现实情况中的聊天,其建立过程为:双方用户在消息交互中知道了对方的Full JID,因此可以直接通信,响应的机制称为chat session。
状态通知
类似于QQ的“正在输入”功能,让交互双方了解即时状态。
该功能扩展由XEP-0085定义。如果用户不希望对方看到自己的状态,可以选择不响应<active/>节。
已经定义好的状态有:Starting,Active,Composing,Paused,Inactive,Gone
消息类似于:
<message from=you@yourdomain.tld/work to=daughter@yourdomain.tld type="chat">
<body>Hi honey!</body>
<active xmlns="http://jabber.org/protocol/chatstates"/>
</message>
格式化消息
可以在message消息中加入XHTML用于富文本展示。
协议可以在[XEP-0071]中找到。
<message from=you@yourdomain.tld/home to=friend@theirdomain.tld type="chat">
<body>I love this movie I saw last night, it's awesome!</body>
<html xmlns="http://jabber.org/protocol/xhtml-im">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>
I <em>love</em>, this new movie I saw last night, it's <strong>awesome</strong>!
</p>
</body>
</html>
</message>
很容易注意到,消息中包含一个不包含格式的文本,以及XHTML格式的文本,是因为考虑到客户端如果不支持HTML,也可以正常展示。
HTML节中不可以包含HEAD中的信息,也不能包含脚本(安全性考虑)。
vCards
也就是虚拟名片,用户在聊天时可以通过虚拟名片查看相关信息。
参考[XEP-0054]
虚拟名片服务通过发送IQ请求查看和更新,但需要注意的是,更新虚拟名片时需要发送完整的信息而不是只有要更新的部分。
查询请求:
<iq from=mouse@wonderland.lit/pool id="pw91nf84" to=alice@wonderland.lit type="get">
<vCard xmlns="vcard-temp"/>
</iq>
返回的消息:
<iq from=alice@wonderland.lit id="pw91nf84" to=mouse@wonderland.lit/pool type="result">
<vCard xmlns="vcard-temp">
<N>
<GIVEN>Alice</GIVEN>
</N>
<URL>http://wonderland.lit/~alice/</URL>
<PHOTO>
<EXTVAL>http://www.cs.cmu.edu/~rgs/alice03a.gif</EXTVAL>
</PHOTO>
</vCard>
</iq>
更新名片:
<iq from=alice@wonderland.lit/pda id="w0s1nd97" to=alice@wonderland.lit type="set">
<vCard xmlns="vcard-temp">
<N>
<GIVEN>Alice</GIVEN>
</N>
<URL>http://wonderland.lit/~alice/</URL>
<PHOTO>
<EXTVAL>http://www.cs.cmu.edu/~rgs/alice03a.gif</EXTVAL>
</PHOTO>
<EMAIL>
<USERID>alice@wonderland.lit</USERID>
</EMAIL>
</vCard>
</iq>
阻塞和过滤通信
类似于QQ可黑名单机制,可以对某人隐身,不看某人的消息或屏蔽某个域的消息等等;当然也包括对名单的修改。
该功能可支持的粒度非常细,参考标准[XEP-0016],[XEP-0191]。
更多消息扩展
- Extended Stanza Addressing[XEP-0033]
发送一条消息给多个接受者而不通过聊天室
- Advanced Message Processing[XEP-0079]
控制消息过期,避免消息被本地存储以及延时投递等
- Message Receipt[XEP-00184]
客户端层面确认消息是否已经送达
- Message Archiving[XEP-0136]
在服务器上存储消息,而不是在客户端机器上存储
Service Discovery
我们需要知道系统中有哪些实体,以及该实体支持哪些服务,为了完成这些操作,引入了实体发现和服务发现的概念。
Items and Info
XMPP 服务发现协议[XEP-0030]定义了两个基本的发现方法。首先发现disco#items,disco#info。
- 向服务器发送发现实体请求
<iq from=hatter@wonderland.lit/home id="xl391n47" to="wonderland.lit" type="get">
<query xmlns="http://jabber.org/protocol/disco#items"/>
</iq>
- 服务器响应发回实体列表
<iq from="wonderland.lit" id="xl391n47" to=hatter@wonderland.lit/home type="result">
<query xmlns="http://jabber.org/protocol/disco#items">
<item jid="conference.wonderland.lit"/>
<item jid="notify.wonderland.lit"/>
</query>
</iq>
- 查询实体支持的功能
<iq from=hatter@wonderland.lit/home id="gq02kb71" to="conference.wonderland.lit" type="get">
<query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>
- 返回实体支持功能结果列表
<iq from="conference.wonderland.lit" id="gq02kb71" to=hatter@wonderland.lit/home type="result">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="conference" type="text" name="Chatrooms"/>
<feature var="http://jabber.org/protocol/muc"/>
<feature var="jabber:iq:register"/>
<feature var="vcard-temp"/>
</query>
</iq>
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 Interaction
MCU 基础
- 多人聊天最初被称为groupchat,后来的迭代版本改进为Multi-User Chat(UMC)[XEP-0045]。
- MCU的基本思想是用户加入到一个聊天室,而聊天室会组播消息,聊天室起到消息反射器的作用
- 聊天室有如下特征
3.1 消息在所有的参与者中共享
3.2 所有的参与者都有一个room roster
3.3 参与者都使用其nickname标识,而不是实际的JabberID
3.4 房间共享参与信息
3.5 参与者不仅限于人,也可以是服务等
- 聊天室有其自己的JID,且该JID是服务器的一个组件,因此具有不同的域,如服务器的域称为:wonderland.lit,组件的域为conference.wonderland.lit,实现MCU需要相应的组件,服务器根据域的不同将消息路由到对应的组件上处理。
- 加入聊天流程
5.1 用户发送presence消息
5.2 聊天室向成员广播该presence
5.3 聊天室向用户发送成员的presence
5.4 聊天室向用户发送一些历史消息好让用户参与讨论,消息数目可配置,且消息带有时间戳
<delay xmlns="urn:xmpp:delay" stamp="2008-11-07T18:42:03Z"/>
5.5 之后的聊天消息不再携带时间戳
5.6 聊天室之间的消息往来,消息类型为groupchat
- 如果用户向聊天室的成员发送消息,消息类型为chat,但消息实际上使用用户发送给聊天室的,聊天室会改写from/to字段为实际接受者的JID。
- 如果离开聊天室则会发送退席消息
<presence from=dormouse@wonderland.lit/sleepspace to=teaparty@conference.wonderland.lit/Dormouse type="unavailable"/>
成员管理
群组中有多重角色,不同的角色拥有不同的权限,可以将用户临时踢出,或加入黑名单等。
具体有: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客户端的设计,尤其是需要减少对服务器端的压力
- 尽量重用已有协议,而不是重新设计一个
基于XMPP的即时通信系统的建立(四)— 协议详解的更多相关文章
- 基于XMPP的即时通信系统的建立(二)— XMPP详解
XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...
- 基于XMPP的即时通信系统的建立(一)— XMPP基础概念
相关背景 IM(Instant Messaging)正在被广泛使用,特别是公司与它们的客户互动连接方案以及互联网与Web2.0相关的应用.为了解决即时通信的标准问题,IETF(互联网工程任务组 The ...
- 基于XMPP的即时通信系统的建立 — XMPP IQ详解
XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...
- 基于XMPP的即时通信系统的建立(三)— 程序设计概览
XMPP与HTTP的比较 XMPP的优势 1. 推送数据 HTTP只能从服务器哪里请求数据,除非服务器正在响应客户端请求,否则不能向客户端发送数据.但XMPP连接是双向的,任何一方在任何时候都 ...
- 基于XMPP的即时通信系统的建立(五)— openfire
现决定使用Openfire作为服务端,Openfire采用Java开发,基于XMPP的实时开源协作服务器.单台可支持上万并发用户. Openfire体系结构 Openfire体系由其提供的服务器端.客 ...
- 基于XMPP的即时通信系统的建立(四)— 组件介绍
服务端 服务器 许可证 操作系统 是否支持任意客户端登录 备注 ejabberd 开源 Elang 是 支持虚拟主机和集群 Openfire Apache Java 是 Tigase GPLv3 Ja ...
- 基于XMPP的即时通信系统的建立(六)— 开发环境搭建
服务器端 新建空工程 使用Eclipse新建名为openfire的空java工程. 导入源代码 这里使用的是openfire的openfire_src_3_10_3.zip源码. 导入后将目录src/ ...
- 基于51的串行通讯原理及协议详解(uart)
串行与并行通讯方式并行:控制简单,传输速度快.线多,长距离成本较高且同时接受困难.串行:将数据字节分成一位一位的行驶在一条传输线上进行传输.如图: 同步与异步串行通讯方式同步串行通讯方式:同步通讯 ...
- Android基于XMPP的即时通讯3-表情发送
这篇博文主要讲表情发送的一些东西. 参考:Android基于XMPP的即时通讯1-基本对话 1.准备好资源文件 采用的是emoji的表情,我打包好了,下载地址:http://files.cnblogs ...
随机推荐
- javascript中alert()与console.log()的区别
我们在做js调试的时候使用 alert 可以显示信息,调试程序,alert 弹出窗口会中断程序, 如果要在循环中显示信息,手点击关闭窗口都累死.而且 alert 显示对象永远显示为[object ]. ...
- linux文件系统创建文件的过程
创建一个文件最主要的步骤就是: 1.为文件创建一个文件目录项. 2.为文件创建一个inode结构并分配inode号,将inode编号与文件名映射关系保存在1中分配的文件目录项中. 3.将1中创建的文件 ...
- 2012 Asia JinHua Regional Contest
Draw Something http://acm.hdu.edu.cn/showproblem.php?pid=4450 o(n)统计输入每个数的平方和. #include<cstdio> ...
- 【转载】C#.Net 创建网页快捷方式
using System.Runtime.InteropServices; using IWshRuntimeLibrary; // 添加引用:COM下Windows Script Host Obje ...
- Properties --- C++读配置信息的类
http://blog.csdn.net/billow_zhang/article/details/4304980 在开发实践中,积累了一些通用的C++ 类库,在此写出来给大家分享.也希望能给出更好的 ...
- What is the Best Programming Language to Learn in 2014?
It’s been a year since I revealed the best languages to learn in 2013. Once again, I’ve examined the ...
- Android开发--Activity生命周期回顾理解
Activity和Servlet一样,都用了回调机制.我们通过类比servlet来学习Activity.当一个servlet开发出来之后,该servlet运行于Web服务器中.服务器何时创建servl ...
- ***iOS 项目的目录结构能看出你的开发经验
最近有师弟去面试iOS开发,他谈论到,面试官竟然问他怎么分目录结构的,而且还具体问到每个子目录的文件名. 目录结构确实很重要,面试官问他这些无疑是想窥探他的开发经验.清晰的目录结构,可让人一眼知道对应 ...
- hdu 4111 Alice and Bob 博弈论
这里有2种方法: 方法一:求SG函数 sg[i][j]:i表示1的个数,j表示合并操作的步数. 这共有4种操作: 1.消除一个1: 2.减掉一个1: 3.合并2个1: 4.把1合并到另外不是1中. 代 ...
- hdu 4686 Arc of Dream
思路:构造矩阵 a[i]*b[i]=ax*bx*a[i-1]*b[i-1]+ax*by*a[i-1]+ay*bx*b[i-1]+ay*by 代码如下: #include<iostream> ...