基于XMPP的即时通信系统的建立(四)— 协议详解

时间:2022-03-09 08:54:48

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

消息发送流程

  1. user1向user2发送消息,user1位于domain1,user2位于domain2
  2. user1的client1向server1发送消息节,server1设置from属性
  3. server1投递消息给server2(直接通信)
  4. server2收到消息并检查user2是否在线并投递
  5. 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 基础

  1. 多人聊天最初被称为groupchat,后来的迭代版本改进为Multi-User Chat(UMC)[XEP-0045]。
  2. MCU的基本思想是用户加入到一个聊天室,而聊天室会组播消息,聊天室起到消息反射器的作用
  3. 聊天室有如下特征

3.1    消息在所有的参与者*享

3.2    所有的参与者都有一个room roster

3.3    参与者都使用其nickname标识,而不是实际的JabberID

3.4    房间共享参与信息

3.5    参与者不仅限于人,也可以是服务等

  1. 聊天室有其自己的JID,且该JID是服务器的一个组件,因此具有不同的域,如服务器的域称为:wonderland.lit,组件的域为conference.wonderland.lit,实现MCU需要相应的组件,服务器根据域的不同将消息路由到对应的组件上处理。
  2. 加入聊天流程

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

  1. 如果用户向聊天室的成员发送消息,消息类型为chat,但消息实际上使用用户发送给聊天室的,聊天室会改写from/to字段为实际接受者的JID。
  2. 如果离开聊天室则会发送退席消息

<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

订阅者需要订阅一个源,发布者也将消息发布到这个源。

流程:

  1. 用户发送订阅请求
  2. 服务器响应该订阅请求

如果成功则可以接收消息了,如果失败,则有可能是要求更多的配置信息

  1. 订阅者对订阅进行配置(可选)
  2. 订阅者请求配置项
  3. 源返回订阅配置表单
  4. 用户解除订阅
  5. 服务器返回解除订阅响应

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

  1. 尽量不要修改XMPP的核心协议,而是应该试着通过新的namespace去扩展它
  2. 由于presence占用了大约90%的数据流量,需要控制presence节中的数据
  3. 需要尽量简化XMPP客户端的设计,尤其是需要减少对服务器端的压力
  4. 尽量重用已有协议,而不是重新设计一个

基于XMPP的即时通信系统的建立(四)— 协议详解的更多相关文章

  1. 基于XMPP的即时通信系统的建立(二)— XMPP详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  2. 基于XMPP的即时通信系统的建立(一)— XMPP基础概念

    相关背景 IM(Instant Messaging)正在被广泛使用,特别是公司与它们的客户互动连接方案以及互联网与Web2.0相关的应用.为了解决即时通信的标准问题,IETF(互联网工程任务组 The ...

  3. 基于XMPP的即时通信系统的建立 — XMPP IQ详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  4. 基于XMPP的即时通信系统的建立(三)— 程序设计概览

    XMPP与HTTP的比较 XMPP的优势 Ÿ   1. 推送数据 HTTP只能从服务器哪里请求数据,除非服务器正在响应客户端请求,否则不能向客户端发送数据.但XMPP连接是双向的,任何一方在任何时候都 ...

  5. 基于XMPP的即时通信系统的建立(五)— openfire

    现决定使用Openfire作为服务端,Openfire采用Java开发,基于XMPP的实时开源协作服务器.单台可支持上万并发用户. Openfire体系结构 Openfire体系由其提供的服务器端.客 ...

  6. 基于XMPP的即时通信系统的建立(四)— 组件介绍

    服务端 服务器 许可证 操作系统 是否支持任意客户端登录 备注 ejabberd 开源 Elang 是 支持虚拟主机和集群 Openfire Apache Java 是 Tigase GPLv3 Ja ...

  7. 基于XMPP的即时通信系统的建立(六)— 开发环境搭建

    服务器端 新建空工程 使用Eclipse新建名为openfire的空java工程. 导入源代码 这里使用的是openfire的openfire_src_3_10_3.zip源码. 导入后将目录src/ ...

  8. 基于51的串行通讯原理及协议详解(uart)

    串行与并行通讯方式并行:控制简单,传输速度快.线多,长距离成本较高且同时接受困难.串行:将数据字节分成一位一位的行驶在一条传输线上进行传输.如图:   同步与异步串行通讯方式同步串行通讯方式:同步通讯 ...

  9. Android基于XMPP的即时通讯3-表情发送

    这篇博文主要讲表情发送的一些东西. 参考:Android基于XMPP的即时通讯1-基本对话 1.准备好资源文件 采用的是emoji的表情,我打包好了,下载地址:http://files.cnblogs ...

随机推荐

  1. Redis慢链接查看

    设置定义慢日志(小于n微秒的定义为慢日志):CONFIG SET slowlog-log-slower-than n 注:1秒 = 1,000,000微秒设置服务器保存的慢日志最多条数:config ...

  2. Qt调用WebService

    从网上查找Qt调用WebService的方案,需要下载三方的类库,而且需要使用好几个控制台命令,才能生成代理客户端类.因为只是简单的测试,没有采用这种方式,直接使用HTTP的Get获取网站内容,也非常 ...

  3. &lbrack;shell基础&rsqb;——sort命令

    sort命令 sort是按照ASCII码升序输出,且是从首字符依次向后比较的 常见选项      -c 测试文件是否已经被排序 -r  逆向排序      -n 按照数字数值大小排序 -t  指定分割 ...

  4. C&plus;&plus;重载(主要介绍使用友元函数重载)

    重载限制 多数C++运算符都可以用下面的方式重载.重载的运算符不必是成员函数,但必须至少有一个操作数是用户自定义的类型.下面详细介绍C++对用户定义的运算符重载的限制. 1 重载后的运算符必须至少有一 ...

  5. typeerror &dollar;&period;ajax is not a function

    在web开发中使用jQuery进行前端开发遇到这么个问题,纠结了很久终于解决了,下面说一下解决方法. 大家可以参照下面几种排查的方法. 1.首先检查是否引用jQuery的库. 2.页面如果使用的ifr ...

  6. keil中查看内存数据

    1.工具栏中 view->Memory Windows 然后  c:0 表示读取0地址开始的代码区数据  d:0 表示读取0地址开始的数据区数据  x:0表示读取0地址开始的外部数据区

  7. Linux 于 shell 变数 &dollar;&num;&comma;&dollar;&commat;&comma;&dollar;0&comma;&dollar;1&comma;&dollar;2 含义解释&colon;

    变量说明: $$ Shell自己PID(ProcessID) $! Shell背景上次执行Process的PID $? 命令的结束代码(返回值) $- 使用Set命令设定的Flag一览 $* 全部參数 ...

  8. 9、JcomboBox下拉框事件监听

    9.JcomboBox下拉框事件监听 JComboBox()的事件监听类ItemListener.其范例代码如下: import java.awt.*; import java.awt.event.* ...

  9. 20&period;1章JSON语法

    1,语法 JSON有三种类型的值 简单值:使用与JavaScript相同的语法,可以在JSON中表示字符串,数值,布尔值,null.但是JSON不支持JavaScript中特殊的值undefined. ...

  10. python之路--类的约束&comma; 异常处理&comma; MD5&comma; 日志处理

    一 . 类的约束 1. 写一个父类,父类中的某个方法要抛出一个异常 NotImplementedError class Base: # 对子类进行了约束. 必须重写该方法 # 以后上班了. 拿到公司代 ...