OpenFire学习:XMPP协议

一、概述

XMPP全称为可扩展通讯和表示协议,是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。 因此,基于XMPP的应用具有超强的可扩展性。
优势:

  • 方便扩展
  • 成熟的通信协议
  • 可快速部署,支持100万的以上在线用户
  • 成熟稳定
  • XMPP的扩展协议Jingle使得其支持语音和视频

二、基本网络结构

XMPP中定义了三个角色,客户端,服务器,网关。通信能够在这三者的任意两个之间双向发生。服务器同时承担了客户端信息记录,连接管理和信息的路由功能。网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端通过TCP/IP)连接到单服务器,然后在之上传输XML。

示例-客户端:

<?xmlversion='1.0'?>
<stream:stream
  to='example_com'
    xmlns='jabber:client'
    xmlns:stream='http_etherx_jabber_org/streams'
version='1.0'>
    <messagefrom='juliet_example_com'
to='romeo_example_net'
xml:lang='zh-cn'>
        <body>你好,可以交个朋友吗?</body>
    </message>
</stream:stream>

示例-服务端:

<?xmlversion='1.0'?>
<stream:stream
  from='example_com'
  id='someid'
    xmlns='jabber:client'
    xmlns:stream='http_etherx_jabber_org/streams'
version='1.0'>
    <message from='romeo_example_net'
to='juliet_example_com'
xml:lang='zh-cn'>
        <body>当然可以</body>
    </message>
</stream:stream>

三、XML使用和说明

XMPP的协议,首先以<stream></stream>标签作为根节点,流的生命周期:标签的开头即是流的开始,标签的关闭即是流的结束。
在stream标签中,数据通过其内部的子标签(message、presence、iq等)当做数据实体,通过子标签可以定义大量的数据。

基本节点

1. presence

presence节点本身表示通知服务器自己的状态,用来控制和表示实体的在线状态,包含:离线(away)、在线()、离开、不能打扰等复杂状态,另外,还能白用来创建和结束在线状态的监听/订阅;
参考文档

在节点中,type字段是可选,为空则表示只是通知服务器自己在线。如果不为空, 则表示自己不在线的原因

  • Presence.Type.unavailable- signals that the entity is no longer available for communication.
  • Presence.Type.subscribe -- the sender wishes to subscribe to the recipient's presence.
  • Presence.Type.subscribed -- the sender has allowed the recipient to receive their presence.
  • Presence.Type.unsubscribe -- the sender is unsubscribing from another entity's presence.
  • Presence.Type.unsubcribed -- the subscription request has been denied or a previously-granted subscription has been cancelled.
  • Presence.Type.probe -- a request for an entity's current presence; SHOULD be generated only by a server on behalf of a user.
  • Presence.Type.error-- an error has occurred regarding processing or delivery of a previously-sent presence stanza.

1.1.子属性

Show

用于指定实体或特定资源的特定可用性状态,在presence中只能存在一个。数据值只能是以下之一:

  • away 表示用户暂时离开
  • chat 表示用户正在聊天
  • dnd 表示用户处于请忽打扰=Do Not Disturb
  • xa 表示用户处于长时间离线=eXtended Away,不在线
<presence>
      <show>away</show> <!--离线-->
</presence>

1.2. Status

针对可用性的描述,通常和Show元素一起使用(例如:在会议中)。

<presence>
      <show>away</show> <!--离线-->
      <status>atthe ball</status> <!--标签用于显示额外信息-->
</presence>

1.3. Priority

用于指定资源的优先级。值在-128到127之间。

2. message

讲限定消息推送到聊天对话或多用户聊天室的消息实体。

2.1 主要属性

属性 说明
from 发送信息自身的Full ID。
to 设置信息接收方的Bare JID,通常在第一次发送方无法得知接收方的Full JID,通过服务器中转路由根据Base JID映射接收方的Full JID,尽量在to属性中包含对方的完成Full JID,减少服务器的接入定位。
id 该属性仅用于接收实体发送给初始化实体 XML流的头。这个属性是一个由接收实体创建的具有唯一性的ID,一个初始实体和接收实体之间的会话ID,并且它在接收方的应用程序中必须是唯一的。注意:这个流 ID 必须是足够安全的,所以它必须是不可预知的和不可重复的。它不应该在有 'id'属性出现在初始实体发送给接收实体的 XML流的头中;无论如何,如果'id'属性出现在初始化流中,接收实体应该忽略它。
xml:lang 用于实现国际化的属性,表示发送的XML字符所使用的语言。
version 代表当前XML实体的版本
type 聊天类型

JID的结构:在XMPP网络上,每一个实体都有一个JID标识,JID是一组排列好的元素,包括域名(domain identifier),节点名(node identifier),和资源名(resource identifier)。如:jid = [ node "@" ] domain [ "/" resource ]

  • node:是对用户的抽象,既可以代表一个真实的用户,也能表示一个虚拟用户如一个聊天室等。
  • domain:表达了客户所连接的服务器,在实践中通常表示一个特定的集群,由同一domain来表示。
  • resource:它通常表示一个特定的会话,连接。对于服务器和和其他客户端来说,资源名是不透明的。

2.2 type

聊天类型:

  • chat -- 在一对一聊天对话的上下文中发送聊天信息,存在历史记录。
  • error -- 表示通知发送者错误的原因,一般由服务端发送给客户端。
  • groupchat -- 用于多人聊天室的消息发送,存在历史记录。
  • headline -- 提供发送指定客户端或广播内容(系统通知、警告、实时数据更新)所采用的类型。具有很高的实时性,预期是不需要收到回复的信息。
  • normal -- 是独立于一对一消息和多人聊天之外的消息,预期是有信息回复的,但是没有历史记录的产生,一般用于系统强制用户确认或取消等。

2.3 子标签

  • <subject/>
  • <body/>
  • <thread/>
  • <delay/>
2.3.1 subject

表示一个消息体的主题内容,通常在聊天窗口标题处。除了xml:lang之外,不会有其他属性。该元素可以有多个(每个lang不能重复,只能存在一个),方便不同语言不通的主题 。

<message
    to='romeo@example.net'
    from='juliet@example.com/balcony'
    type='chat'
    xml:lang='en'>
  <subject>I implore you!</subject>
  <subject
      xml:lang='cz'>&#x00DA;p&#x011B;nliv&#x011B; prosim!</subject>
  <body>Wherefore art thou, Romeo?</body>
  <body xml:lang='cz'>Pro&#x010D;e&#x017D; jsi ty, Romeo?</body>
</message>
2.3.2 body

指定消息的文本内容。除了xml:lang之外,不会有其他属性。该元素可以有多个(每个lang不能重复,只能存在一个),方便不同语言不通的显示 。

<message
    to='romeo@example.net'
    from='juliet@example.com/balcony'
    type='chat'
    xml:lang='en'>
  <body>Wherefore art thou, Romeo?</body>
  <body xml:lang='cz'>Pro&#x010D;e&#x017D; jsi ty, Romeo?</body>
</message>
2.3.3 thread

用于跟踪一个会话,该元素主要用于客户端实现消息展示(例如:消息历史查询时,每次会话折叠显示消息),每次会话会产生一个唯一的thread.id,xmpp推荐采用uuid算法。不同时间段的聊天内容,可能是基于多个会话,在查询聊天记录的时候,可以根据会话ID进行折叠显示。

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

2.3.4 delay

表示该消失是一个离线消息。 <delay>子元素的from记录了延迟消息的最后来源方,如上例中from为capulet.com指接收离线消息人连接的服务器,离线消息最终由该服务器发出
stamp属性记录了离线消息的存储时间,客户端实现应显示该时间而非接收到的时间。

<message from='romeo@montague.net/orchard' to='juliet@capulet.com'>
  <body>
    收到,请回复信息。
  </body>
  <delay xmlns='urn:xmpp:delay'
     from='capulet.com' 
     stamp='2002-09-10T23:08:25Z'>Offline Storage</delay>
</message>

3. IQ

iq节点主要是用于Info/Query模式的消息请求,类似于HTTP的get/post请求,可以发出get以及set请求,期望有返回值(有result,error两种回应)。

type属性值:

  • Get: 获取当前域值
  • Set: 设置替换get查询的值
  • Result: 说明成功相应了先前的查询
  • Error: 查询或相应时候出现了错误
<iq to='example.com'
    type='set'
    id='sess_1'>
  <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</iq>
image.png

4. 通信流程

使用Stream元素,用来表示客户端-服务器-客户端的通信建立,在建立通信时,需要保证以下两点:

  • 在创建会话或者发送信息之前,必须完成流的认证。

  • 在完成流的认证之后,客户端必须将资源绑定到流,如客户端地址<user@domain/resource>

  • 服务端向客户端通知会话建立功能;

<stream:stream
    xmlns='jabber:client'
    xmlns:stream='http://etherx.jabber.org/streams'
    id='c2s_345'
    from='example.com'
    version='1.0'>
<stream:features>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
  <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>
  • 1、客户端向服务端发起会话请求,并指定一个绑定的资源名:
<iq to='example.com'
    type='set'
    id='sess_1'>
  <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>  
    <resource>pc-win-someone</resource>  
  </bind>  
</iq>
  • 2、服务端响应资源绑定请求,并返回绑定后的Full JID名:
<iq from='example.com'
    type='result'
    id='sess_1'>
  <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>  
     <jid>somenode@example.com/pc-win-someone-server-gen-random-string</jid>  
   </bind>  
</iq>

在会话创建有几种错误可能:

  • 2.1、服务内部错误 error:type="wait"
  • 2.2、账户错误或者账户不允许会话创建 error:type="auth"
  • 2.3、会话冲突 error:type="cancel"

资源绑定:

  1. 标识客户端的平台
  2. 服务器为每个客户端生成随机值,生成唯一后缀,用于区分不同的客户端连接
  3. 相同账户的多点登录(多个终端登录),通过resource区分同一用户的不同接入点,方便策略的执行

完整数据流

1: 客户端初始化流给服务器:
   <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>

2: 服务器发送一个流标签给客户端作为应答:
   <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_123' from='example.com' version='1.0'>

3: 服务端发送TLS流特征说明(包括验证机制和任何其他流特性):
   <stream:features>
     <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
       <required/>
     </starttls>
     <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
       <mechanism>DIGEST-MD5</mechanism>
       <mechanism>PLAIN</mechanism>
     </mechanisms>
   </stream:features>

4: 客户端发送 TLS握手给服务器:
   <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>

5: 服务器通知客户端可以继续进行:
   <proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
  (或者): 服务器通知客户端 TLS 握手失败并关闭流和TCP连接:
   <failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
   </stream:stream>

6: 客户端和服务器尝试通过已有的TCP连接完成 TLS 握手. resume是否允许恢复会话
   <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true'/>

7: 如果 TLS 握手成功, 客户端初始化一个新的流给服务器:
   <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
  (或者): 如果 TLS 握手不成功, 服务器关闭 TCP 连接.

8: 服务器发送一个加密流初始化给客户端,其中包括任何可用的流特性:
   <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='c2s_234' version='1.0'>
8.1:服务端发送SASL特征说明,mechanism支出了服务支持的认证机制,有关SASL认证机制[RFC4422]
   <stream:features>
     <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
       <mechanism>DIGEST-MD5</mechanism>
       <mechanism>PLAIN</mechanism>
       <mechanism>EXTERNAL</mechanism>
     </mechanisms>
   </stream:features>

9: 客户端继续 SASL 握手
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AGp1bGlldAByMG0zMG15cjBtMzA=</auth>  

5. 多用户文本聊天协议(Multi User Chat)

XMPP在其XEP-0045扩展中定义了一个用于多用户文本会议(群聊)的协议,类似于聊天室、QQ群等。由于它作为一个标准协议在定义模型上力求完备,涵盖了现实中的绝大部分IM产品模型,而现实中的IM产品基本都只实现了XMPP定义的模型中的一个子集。
XMPP定义的一些基本概念:

  • 房间:房间的JID标识room@service,room标识房间的名称或者ID,service是服务器地址
  • 访客: 访客JID<room@service/nick>,nick是访客在房间的昵称
  • 岗位:表达了用户和房间的长期关系。XMPP定义的岗位有:所有者(owner)、管理者(admin)、成员(member)、排斥者(outcast)
  • 角色:表达了用户和房间的临时联系,它只存在与一次访问期间。角色:主持人(moderator)、与会者(paticipant)、游客(visitor)

XMPP MUC协议扩展定义了一个广泛的用例集合,下面提取一些典型的核心场景来简要分析说明并辅助实现。

  1. MUC服务发现
    主要用于客户端向服务器咨询是否支持MUC,协议交互细节详见:MUC Discovering
  2. 新建房间
    从房间创建的视角来看,本质上有2种类型的房间:
    instant room 临时房间(类似于临时会话),适用于那些临时选取多个用户进行会话的场景
    reserverd room 永久房间(类似于固定群)
  3. 销毁房间
    销毁房间通常仅限于房间的所有者,临时房间通常是在房间所有用户都离开后自动销毁
  4. 加入房间
    加入房间可以有2种方式,申请和邀请
  5. 发言
    在房间内发言方式从使用场景的角度看通常有3种:
  • 向房间内所有人发言,发言者发送一个消息类型为groupchat的消息,由房间服务转发给所有与会者。
  • 向部分人发言,这个场景发言者实际创建了一个临时房间,在该临时房间内进行群发。
  • 向某一个人发送似有消息,这个场景退化为了一对一的单独聊天。
  1. 退出房间
    主动退出、管理员(主持人)踢出房间

6. 扩展协议

扩展协议

参考文档:

中文翻译
说明文档-英文版
安全验证

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容