转载自 https://www.cnblogs.com/jiyuqi/p/5085932.html

相关背景

IM(Instant Messaging)正在被广泛使用,特别是公司与它们的客户互动连接方案以及互联网与Web2.0相关的应用。为了解决即时通信的标准问题,IETF(互联网工程任务组 The Internet Engineering Task Force)成立了专门的小组、研究和开发IM相关协议。

通信协议

PRIM

空间和即时信息协议(Presence and Instant Messaging)

该协议是IETF撰写的关于即时通讯标准协议的早期版本,其抽象模型最早是在2000年2月份提出的RFC2778中提出的。

从2001年起,该项目无任何进展。现在SIP协议以及其衍生出的SIMPLE以及XMPP协议才被认为是即时消息协议的标准实现。

具体参考:

https://en.wikipedia.org/wiki/Presence_and_Instant_Messaging

IMPP

即时信息与空间协议(Instant Messaging and Presence Protocal)

IMPP主要定义必要的协议和数据格式,用来构件一个具有空间接收、发布能力的即时消息系统。到目前为止,IETF已经出了三个草案的RFC,但主要有两个:一个是针对站点空间和即时通讯模型的(RFC 2778);另一个是针对即时通讯/空间协议需求条件的(RFC2779)。RFC2778是一个资料性质的草案,定义了所有presence和IM服务的原理。RFC2779定义了IMPP的最小需求条件。另外,这个草案还就presence服务定义了一些条款,例如运行的命令、信息的格式、以及presence服务器如何把presence的状态变化通知给客户。

具体参考:

https://en.wikipedia.org/w/index.php?title=Instant_Messaging_and_Presence_Protocol&redirect=no

SIP(SIMPLE)

  • SIP(Session Initiation Protocol)

SIP一种用于信令和控制多媒体通讯session的协议。其最常见的应用是通过SIP协议实现的语音和视频网络电话,以及通过IP网络实现的即时消息通讯。该协议规定了在各通信方中传递的消息,这些消息决定这一个网络电话的建立、终止以及其他必要的步骤。SIP协议可以用于创建、修改和终止一个或多个媒体连接。SIP是一个应用层协议,被设计为独立于下层的传输层。它是一个基于文本的协议,结合了很多HTTP和SMTP协议的元素。SIP协议通常与其他应用层协议一起工作。

  • SIMPLE(The Session Initiation Protocol for Instant Messaging and Presence Leveraging Extensions)

SIMPLE协议是由IETF定义的基于SIP的一个即时消息(IM)和空间协议套。与现今广泛使用的软件实现即时通信与空间协议相比,SIMPLE是类似于XMPP的公开标准。

SIMPLE使用SIP协议发送presence信息。SIP是IETF为终端定制的一种协议,其一般用于建立语音通话中,一旦建立连接后,使用实时协议(RTP)进行实际上的语音发送。但SIP不仅可以用在语音上,也可以用在视频上。SIMPLE被定义为一个建立IM进程的方法。

XMPP

可扩展的消息与空间协议(Extensible Messaging and Presence Protocol)

该协议的前身是Jabber,我们采取XMPP协议主来实现IM主要是考虑XMPP协议是以XML为基础的,它继承了在XML环境中灵活的发展性。这表明XMPP是可扩展的,所以XMPP信息不仅可以是简单的文本,而且可以携带复杂的数据和各种格式的文件,也就是说XMPP协议不仅可以用在人与人之间的交流,而且可以实现软件与软件或软件与人之间的交流,目前支持XMPP协议的即时通讯工具有Gtalk、FaceBook IM、Twitter、网易POPO等等通讯工具。

协议选型

在以上四种协议中,XMPP协议是最为灵活的,XMPP协议基于XML,因此具有良好的扩展性,经过扩展后的XML协议可以发送扩展信息处理用户的需求,以及在XMPP顶端建立如内容发布系统和基于地址的服务等应用程序。而且XMPP包含了针对服务器的软件协议,使之能与另外一个通话,这使得开发者更容易建立客户应用程序或给一个配置好的系统添加功能。

另外,XMPP协议在业界已有成熟的开源实现方案,从开发成本上来讲也是很有优势的。

开源实现已经实现了客户端和服务器端的协议解析逻辑。用户只需要在此基础上开发自己的业务就可以了。

XMPP与HTTP的比较

XMPP的优势

1. 推送数据

HTTP只能从服务器哪里请求数据,除非服务器正在响应客户端请求,否则不能向客户端发送数据。但XMPP连接是双向的,任何一方在任何时候都可以向另外一方发送数据,只要连接是打开状态。

2. 防火墙友好

XMPP对防火墙和NAT友好,因为服务器到客户端的连接是由客户端发起的,一旦连接,服务器就可以将其所需要的数据推送给客户端。

3. 增强的安全性

XMPP构建在TLS和SASL上,具有更好的安全性

4. 更加丰富的工具箱

XMPP支持更多的操作

XMPP的不足

1. XMPP是有状态协议

2. XMPP的生态系统较小

3. XMPP有更大的开销

XMPP未对短期会话和简单请求进行优化,建立、维护和销毁XMPP都需要一些资源。

对于更长的连接和更加复杂的交互,XMPP和HTTP的开销可以忽略不计。

桥接XMPP与Web

主流浏览器暂时没有支持XMPP协议,但是可以通过设置和编程在HTTP连接上建立高效的XMPP会话通道。

这种通道的建立使用到了名为HTTP长连接的技术。通过联合使用一个简单的基于HTTP的管理协议以及XMPP连接管理器,我们可以将XMPP(所有功能)带入到HTTP应用程序中。

长轮询

是早期定时刷新网页获取数据更新的方法改进,服务端通过维持一个空连接,即时通知客户端数据变更。

因为每个请求均可以持续较长时间,因此称为长轮询。

人们已经设计了多个库和协议来利用长轮询技术,XMPP是其中一个较早实现,在XMPP中,这种桥接被称为BOSH(Bidirection stream Over Synchronous HTTP,在同步HTTP上传递双向数据流)。

管理连接

XMPP连接可以持续相当长的时间,但HTTP请求确相当短。连接管理器负责维护第三方的XMPP连接并通过HTTP长连接技术提供对连接的访问。

浏览器和连接管理器都使用BOSH协议通过HTTP通信,并提供了安全功能,连接管理器与服务端通信就像一个普通的客户端一样,延时也比较低。

而且连接管理器还有如下的优势:

客户端可以切换网络

托管稳固,可以容忍故障

可好性好,连接管理器缓存数据

对防火墙友好(运行与HTTP端口)

让Javascript理解XMPP协议

通过Javascript的Strophe库来实现,可以隐藏底层细节并具有很好的效率。

构建XMPP应用程序

浏览器平台

可以选用jQuery操作用户数据及用户界面。

XMPP服务器

可以选用Tigese或者OpenFire

协议扩展

如果有扩展协议的需求,应该首先试着组合现有协议或者将协议设计的尽量简洁。

服务端

服务器

许可证

操作系统

是否支持任意客户端登录

备注

ejabberd

开源

Elang

支持虚拟主机和集群

Openfire

Apache

Java

Tigase

GPLv3

Java

支持虚拟主机和集群

客户端

Spark

使用Java开发,具备跨平台性

浏览器

strophe.js

现决定使用Openfire作为服务端,Openfire采用Java开发,基于XMPP的实时开源协作服务器。单台可支持上万并发用户。

Openfire体系结构

Openfire体系由其提供的服务器端、客户端以及相应的开发库组成。

Openfire

通常我们所说的Openfire就是指Openfire服务端,现在已经更新到3.10.3版本,该服务器实现了绝大部分的XMPP协议。

除此之外,还有大量的Openfire插件可供使用。

Spark

Spark是一个使用java开发的实时协作客户端,通过简单的配置和注册即可使用。

Smack

Smack is a client library. You can use it to log into an XMPP domain and make use of the functionality it provides. Typically, Smack is used to implement an instant messaging client。

Tinder

Tinder is a low-level XMPP library. It provides Java implementations of basic XMPP entities, such as Stanzas (called 'Packet' in XMPP), JIDs and Components (which typically implement part of the functionality of an XMPP server). Although in theory, Tinder could be used to implement client functionality, it currently is primarily used on the server-side. Both Whack and Openfire (an XMPP server implementation) use Tinder-defined entities. Smack does not use Tinder at all.

Whack

Whack is Java library that is used to implement external components. Whack allows a Component (as defined in Tinder) to run as a stand-alone process. Whack allows you to connect this process to an XMPP domain. This way, you can extend the functionality provided by that domain without modifying the implementation of the server software.

服务端搭建

安装数据库

1. 安装客户端

  rmp –ivh MySQL-client-5.6.21-1.linux_glibc2.5.x86_64.rpm

  (注)如果提示冲突,需要运行yum –y remove ${APP}卸载冲突软件

2. 安装服务端

  rmp –ivh MySQL-server-5.6.21-1.linux_glibc2.5.x86_64.rpm

3. 数据库配置

  3.1设置本地登录账号

    mysql –u root -p

    set password for root@localhost=password('jiyq');

    如果主机名其他,例如jiyq,需要将localhost改为jiyq

    set password for root@jiyq=password('jiyq');

  3.2切换数据库

    use mysql;

  3.3设置网络连接账户

    Grant all privileges on *.* to 'root'@'%' identified by 'jiyq' with grant option;

  3.4 更新账户权限

    FLUSH PRIVILEGES; 

  3.5 打开防火墙3306端口

    iptables -A INPUT -p tcp --dport 3306 -j ACCEPT

    iptables -A OUTPUT -p tcp --sport 3306 -j ACCEPT

    service iptables save

  3.7 创建数据库

    create database openfire

  3.8 运行建表语句

    在openfire数据库下,运行/opt/openfire/resources/database/openfire_mysql.sql

安装服务端

1. 解压openfire_3_10_3.zip到/opt目录下

2. 启动openfire

  /opt/openfire/bin

  ./openfire start

3. 访问配置页面

  http://192.168.189.131:9090/setup/index.jsp

4. 配置语言

  

5. 服务器设置

  

6. 数据库设置

  

7. 数据库设置-标准连接

  

8. 特性设置

  

9. 管理员账户

  

8. 安装完成

  

Spark客户端搭建

这里使用Spark作为PC端的客户端。

  1. 安装Spark

    这里采用spark_2_7_3_online.exe版本

  2. 注册用户2个用户

    

  3. 登录客户端,并互添加好友

    

通过代码自定义实现客户端

package com.song.controller;

import org.apache.commons.lang.StringUtils;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;

/**
 * Created by huang(jy) on 2018/3/20.
 */
public class SmackClict {

public static void main(String[] args) {
        try {
            String serviceName = "192.168.20.125";        //openfire服务器名称
            String host = "192.168.20.125";                        //openfire服务器地址
            int port = 5222;                                            //客户端到服务器,默认5222
            String myUsername = "zyy";                    //帐号
            String myPassword = "123456";                //密码
            String friendUsername = "huang";            //要发消息的对象名称
            XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
            builder.setUsernameAndPassword(myUsername, myPassword);
            builder.setServiceName(serviceName);
            builder.setHost(host);
            builder.setPort(port);
            builder.setSecurityMode(SecurityMode.disabled);
            builder.setDebuggerEnabled(false);
            XMPPTCPConnectionConfiguration config = builder.build();
            AbstractXMPPConnection c = new XMPPTCPConnection(config);
            c.connect();
            c.login();
            System.out.println("Authenticated = " + c.isAuthenticated());
            ChatManager chatmanager = ChatManager.getInstanceFor(c);
            Chat newChat = chatmanager.createChat(friendUsername + "@" + serviceName);
            final ChatMessageListener messageListener = new ChatMessageListener() {
                @Override
                public void processMessage(Chat arg0, Message message) {
                    String messageBody = message.getBody();
                    if (StringUtils.isNotBlank(messageBody)) {
                        try {
                            arg0.sendMessage("你刚说的是:" + messageBody);
                        } catch (Exception e) {
                        }
                    }
                }
            };
            ChatManagerListener chatManagerListener = new ChatManagerListener() {
                @Override
                public void chatCreated(Chat chat, boolean arg1) {
                    chat.addMessageListener(messageListener);
                }
            };
            chatmanager.addChatListener(chatManagerListener);
            try {
                newChat.sendMessage("我来咯");
            } catch (Exception e) {
                System.out.println("Error Delivering block");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

刚开始的时候在c.login()处一直报空指针的错,后来查证后是因为少引了个jar,引用jar如下

<dependency>
    <groupId>org.igniterealtime.smack</groupId><!--login时报空指针错就是因为没引这个jar-->
    <artifactId>smack-java7</artifactId>        
    <version>4.1.8</version>
</dependency>

<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-tcp</artifactId>
    <version>4.1.8</version>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-im</artifactId>
    <version>4.1.8</version>
</dependency>

spark客户端和自定义客户端通信

通过spark登录其中一个帐号,通过自定义代码登录另一个帐号

然后给spark客户端帐号发送消息(直接运行main函数),消息发送成功

XMPP详解

XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议。在此基础上,XMPP协议已经被用来构建大规模即时通信系统、游戏平台、协作空间及语音和视频会议系统。

XMPP由几个小的构造块组成,并在此基础上扩展出了更多的构造块。XMPP中有众多系统:发布-订阅服务、多人聊天、表单检索与处理、服务发现、实时数据传输、隐私处理及远程过程调用等。

大多数社交媒体(Facebook及Twitter)也采用了XMPP协议。

什么是XMPP

与其他协议一样,XMPP定义了在两个或者更多通信实体间传递数据所采用的格式。对于XMPP,实体通常是指客户端服务器,但是其也允许客户端与客户端或服务器端与服务器端的通信。

在XMPP上交换的是XML数据,采用这种格式,使XMPP协议获得了极大的可扩展性,因为使用XML可以方便的新增功能并保证前后向兼容。使用XML较二进制协议占用更大的带宽,但获得的优势是具有了几乎无限的可扩展性。

用户可以向XMPP Standards Foundation注册协议扩展。

在XMPP中,XML数据被组织为了一对流,每个流分别对应通信的一个方向。每个XML流均由一个开始元素、后跟XMPP节和其他顶级元素,然后是一个结束元素组成。每个XMPP节(可带有子元素及属性)均是该流的一级子元素。在XMPP连接末尾,这两个流形成了一对有效的XMPP文档。

XMPP节构成了该协议的核心部分,而XMPP应用程序则关注如何发送和响应各种类型的节。节可能包含网络上其他实体的信息、类似于电子邮件的个人消息或为计算机处理而设计的结构化数据。

<message to=’elizabeth@longbourn.lit’ from=’darcy@pemberley.lit/dance’ type=’chat’><body>what think you of books</body></message>

在一个典型的XMPP会话中,一个上述的节将会从darcy的XMPP客户端发送到她的XMPP服务器,她的服务器将会注意到该节的目的地是某个远程服务器上的一个实体,因此与该远程服务器先建立XMPP连接,并将消息转发该处。

这种通信网络与电子邮件类似,但与电子邮件服务器不同的是,XMPP间的服务器可以直接通信,而不需要借助中间服务器。

这种直接通信避免了垃圾信息的干扰,且还支持通过TLS(Transport Layer Security,传输层安全)来加密通信并通过SASL(Simple Authentication and Security Layers,简单身份验证与安全层)实现身份验证机制。

XMPP使用传递短小信息来设计的,而非针对大型数据块,但XMPP能够用来协商并建立可在端点间传递大型数据块的带内或者带外传输。

XMPP历史

最初各大公司发布的ICQ均基于其公司运营的专有协议和网络(ICQ,Yahoo Pager)。

开发人员企图互通这些IM的努力由于协议的闭源特性失败了。

1991年1月Jeremie Miller发布了Jabber项目,Jabber是基于XML的去中心化的即时通信协议,同时也是一个叫jabberd的服务器实现。

2000年5月,Jabber的核心协议稳定,jabberd也正式发布了。

2001年,JSF(Jabber Software Foundation,Jabber软件基金会)成立,并开始围绕jabber协议做规范性工作。

2002年12月,JSF向IETF提交了核心规范,并成立了一个IETF小组。

2004年10月,这个标准化进程产生了Jabber协议的改进版,改名为XMPP,其文档成为RFC标准,编号分别为3920、3921、3922和3923。

最初开发人员向JSF提交的扩展称为JEP(Jabber Extension Proposal,Jabber扩展提议)。最终随着Jabber过度到XMPP,JSF更名为XSF(XMPP Standard Foundation,XMPP标准基金会)和XEP(XMPP Extension Proposal,XMPP扩展提议)。

2005年XMPP技术开始大规模部署,例如Google Talk。

先进有大约300个XEP,并有数十种客户端和服务端实现,开源及商用均有,实际上,任何编程语言都可以找到这样一个库来加速XMPP开发进程。

XMPP网络

任何XMPP网络都是由若干角色组成,可以分为服务器端、客户端、组件和服务器插件。

XMPP网络与WWW网络及EMAIL网络不同,XMPP服务器之间寻址只会跳一次,而EMAIL协议则会有多个中转服务器,XMPP保存完整的列表。

服务器

XMPP服务器是任何XMPP网络的通信系统,服务器的任务就是为XMPP节提供路由。无论这些节是从内部的一个用户发往另外一个用户还是本地用户发送给服务器。

一组能够相互通信的XMPP服务器构成了XMPP网络。

XMPP服务器总是允许用户连接到自己,但是也可以编写直接使用服务器-服务器协议的应用和程序,来减轻路由消耗。

Ejabberd、Openfire和Tigase是三种能够运行在Windows,Mac OS X和Linux的开源服务器。

M-Link和Jabber XCP是商用产品。

客户端

大多数XMPP实体均是客户端,通过客户端-服务器协议连接到XMPP服务器。

客户端必须向某个地方的XMPP服务器进行身份验证。服务器会将该客户端发送的所有节路由到合适的目的地。

服务器还负责管理客户端会话的其他几个方面,包括花名册及裸地址。

组件

不仅仅是客户端能够连接到XMPP服务器,大多数服务器还支持外部服务器组件。这些组件通过添加某种新服务来增强服务器的行为。这些组件在服务器内有各自的身份和地址,但运行在外部并通过组件协议通信。

组件协议(XEP-0114)可以让开发人员以一种服务器不可知的方式创建服务器扩展,例如多人聊天服务。

组件也需要向XMPP服务器进行身份验证,但要较客户端的完全SASL验证简单,例如口令。

每个组件编程服务器内部一个可单独寻址的实体,在外界看类似于一个子服务器。除了基本节之外,XMPP服务器不会代替已连接组件来管理其他节的路由。

服务器还允许组件在内部自行路由或管理节,因而更为灵活。

插件

许多XMPP服务器还支持使用插件进行扩展,但插件深入到服务器内部,有较高的效率以及最低的通用性。

插件一般是绑定特定类型的服务器的。

XMPP寻址

XMPP网络上的每个实体都有一个或多个地址(称为JID,jabber identifier)。通常类似于:

darcy@pemberley.lit和elizabeth@longbourn.lit就是两个JID。

JID由三个部分组成,节点、域和资源,域是必须的,其他两个部分是可选的。

域是实体(服务器、组件或插件)可解析的DNS名称。仅由域组成的JID是有效地址,表示服务器地址。指向域的节将由服务器自身处理,并可能被路由到某个组件或插件。

本地部分通常用来识别域中的一个特定用户,位于@前。本地部分也可以用来识别其他对象,如某个聊天室。

JID的资源部分通常会标识一个特定客户端的XMPP连接。对于XMPP客户端而言,每个连接均被指派一个资源。如darcy@perberley.lit想要连接他的书法和图书馆则可以通过

darcy@perberley.lit/study和darcy@perberley.lit/library来寻址,这样避免了用户在打开多个链接时消息无法找到正确的处理器。主要注意的是,资源部分是区分大小写的。

JID划分为两种类型:

  • 裸JID

完整JID去除资源部分的地址,客户端的裸JID有些特殊,这是因为服务器自己将处理发往客户端的裸JID节。裸JID可以视为寻址用户的账户,而不是客户端。

  • 完整JID

最为具体的地址

XMPP节

核心XMPP工具集由三个基本节组成,分别为<presence>、<message>和<iq>

XMPP流由两份XML文档组成,通信的每个方向均有一个文档,这些文档有一个根元素<stream:stream>,<stream:stream>的子元素由可路由的节以及与流相关的顶级子元素构成。

<stream:stream><iq type=’get’><query xmlns=’jabber:iq:roster’ />  //请求自己的花名册</iq><presence/>  //通知服务器她已在线并可以访问<message to=’darcy@pemberley.lit’ from=’elizabaeth@longbourn.lit/ballroom’ type=’chat’><body>I cannot talk of books in a ball-room; my head is always full of something else.</body> //发送消息</message><presence type=’unavailable’> // 声明自己不可访问并关闭
</stream:stream>

通用属性

from/to/type/id

from的属性并非由客户端提供,而是服务端进行的标记。

presence节

presence提供网络实体的可访问性。用户发出presence节,表明自己上线,这样可以会有更大的概率与别人通信(人们更愿意与在线的人交流),但是我们也不用担心任何人都可以看到自己的在线状态,除非我们订阅了该用户的状态,订阅之后,用户的状态信息会自动发送到订阅者处。

实际上,XMPP的presence节是一个简单的专用的发布-订阅方法。

在IM中,presence体现在花名册(roster)中,花名册保存有JID列表以及用户与这些JID的订阅关系,一旦上线,用户发送presence节,剩下的就由服务器处理了(通知自己在线,以及获取联系人的状态信息)

message节

用于从一个实体向另外一个实体发送消息,并可以传输任何类型的结构化信息,不保证传输可靠性

message是一个非常基础的推模型,message通常用于IM,groupchat,警告和通知等。

message的type有如下几种:

  • normal

    类似于email,发出后不等待回应

  • chat

    用于两个实体间的实时通信

  • groupchat

    多用户聊天室中使用

  • headline

    用于发送警告或通知

  • error

发送错误信息

<message from=madhatter@wonderland.lit/foo to=alice@wonderland.lit type="chat"><body>Who are you?</body><subject>Query</subject>
</message>

除了type之外,典型的message节中还包含from、to或者id属性(用于目的追踪)。

to中的JID为消息的接受者,from是发送者的JID,但是from属性并非由客户端提供,而是发送者的服务端提供的,以避免地址模仿。

message节中也可以包含未在XMPP协议中定义的负载,可以用于扩展。

IQ节

表示Info/Query,为XMPP通信提供请求及响应机制,类似于GET/POST/PUT方法。

IQ只能包含一个payload,并且定义了需要由服务器处理的请求或者动作。相对于message来说,IQ具有更好的可靠性,因其要求收到回应。

IQ中包含有id属性,用于识别服务器发回的响应。

  • get

    用于请求信息,类似于HTTP Get

  • set

    提供信息或请求,类似于HTTP POST/PUT

  • result

    响应请求,类似于HTTP 200

  • error

    错误信息

例子

  • 发送获取花名册请求
<iq from=alice@wonderland.lit/pda id="rr82a1z7" to=alice@wonderland.lit type="get">
<query xmlns="jabber:iq:roster"/>
</iq>
  • 服务器返回花名册
<iq from=alice@wonderland.lit id="rr82a1z7" to=alice@wonderland.lit/pda type="result"><query xmlns="jabber:iq:roster"><item jid="whiterabbit@wonderland.lit"/><item jid="lory@wonderland.lit"/><item jid="mouse@wonderland.lit"/><item jid="sister@realworld.lit"/></query>
</iq>            
  • 用户新增一个联系人
<iq from=alice@wonderland.lit/pda id="ru761vd7" to=alice@wonderland.lit type="set"><query xmlns="jabber:iq:roster"><item jid="madhatter@wonderland.lit"/></query>
</iq>
  • 服务器响应
<iq from=alice@wonderland.lit id="ru761vd7" to=alice@wonderland.lit/pda type="result"/>

error节

具有明确的结构,通常包含原节内容,通用错误信息以及应用程序特有的错误条件和信息(可选)

可扩展

XMPP协议是基于XML的协议,因此其天生提供了很好的可扩展性。我们可以用XMPP传递各种信息,包括链接、位置信息,Web Service等。

连接生命周期

发送XMPP节通常需要建立一个经过身份验证的XMPP会话,包括连接、流的建立、身份验证以及断开连接。

连接

在发送任何节之前,需要建立XMPP流,在XMPP流存在之前,必须建立通往XMPP服务器的连接。

当XMPP客户端或者服务器连接到另外一个XMPP服务器时,首先要查询SRV记录,该记录保存有特定域的服务器列表。查询应答中可以包含多条SRV记录,这样就可以在多个服务器中建立负载均衡连接。

如果没有找到合适的SRV记录,那么程序将试图直接连接到指定域。

流的建立

一旦建立通过给定XMPP服务器的连接,XMPP流就启动了

向服务器发送<stream:stream>,就可以打开XMPP流,服务器发送响应流的起始标记<stream:stream>进行响应

建立XMPP流之后就可以来回发送各种元素

服务器发送<stream:feature>元素,列举XMPP流中支持所有功能,大多数与可用的加密和身份验证选型有关

身份验证

XMPP允许进行TLS(Transport Layer Security,传输层安全)加密,而且大多数客户端默认使用该功能。

一旦服务器通告TLS支持后,客户端就会启动TLS连接并将当前套接字升级为加密套接字而不断开连接。一旦TLS加密确立,就会创建一对新的XMPP流。

XMPP中的身份验证使用SASL(Simple Authentication and Security Layers,简单身份验证与安全层)协议并支持多种身份验证机制(取决于服务器)。

一旦完成身份验证,客户端必须为连接绑定一个资源并启动一个会话,通过<bind>和<session>元素发送。

当两台服务器相互连接时,身份验证步骤稍稍不同。

连接断开

当用户结束XMPP会话后,他们终止会话并断开连接,最优雅的方式是首先发送无效出席信息,然后关闭<stream:stream>元素。

<presence type=’unavailable’>
</steam:stream>

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的即时通信

    一.Openfire服务器和Spark客户端的安装 1.在下列下载地址下载Openfire和Spark http://www.igniterealtime.org/downloads/index.js ...

  2. XMPP协议简单介绍

    2019独角兽企业重金招聘Python工程师标准>>> 由于博客迁移至www.coderyi.com,文章请看http://www.coderyi.com/archives/434 ...

  3. XMPP协议的原理介绍

    XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测.它在促进服务器之间的准即时操作.这个协议可能最终允许因特网用户向因特网上的其他任何人发送 ...

  4. 基于XMPP协议的Android即时通信系

    2019独角兽企业重金招聘Python工程师标准>>> 以前做过一个基于XMPP协议的聊天社交软件,总结了一下.发出来. 设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过 ...

  5. Android 模块 -- 基于XMPP协议的手机多方多端即时通讯方案

    目   录 基于XMPP协议的手机多方多端即时通讯方案................................................................. 1 目   录 ...

  6. 基于XMPP协议的手机多方多端即时通讯方案

    原文地址为: 基于XMPP协议的手机多方多端即时通讯方案 基于XMPP协议的手机多方多端即时通讯方案 目   录 基于XMPP协议的手机多方多端即时通讯方案...................... ...

  7. 网络协议从入门到底层原理(11)网络爬虫、无线网络、HTTP缓存、即时通信、流媒体

    补充知识 网络爬虫 网络爬虫的简易实例 robots.txt 无线网络 HTTP 缓存(Cache) 缓存 - 响应头 缓存 - 请求头 缓存的使用流程 即时通信(IM) XMPP MQTT 流媒体 ...

  8. android 通过xmpp即时聊天客户端往服务器发消息,利用XMPP协议推送服务器告警信息到安卓平台及桌面...

    XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议. XMPP目前被IETF国际标准组织完成了标准化工作.标准化的核心结果分为两部分: 核心的XML流传输协议 基于XML流传输的即时 ...

  9. 即时聊天IM之一 XMPP协议简述

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q  Q:408365330     E-Mail:egojit@qq.com  综述: ...

最新文章

  1. 15 种 SQL 优化中,老司机才懂的处理技巧
  2. springboot获取payload_Spring Boot 使用 JSR303 实现参数验证
  3. 给大学生分享一下我的编程人生
  4. 音视频技术开发周刊 | 204
  5. 放大镜制作(2)—此方法比较容易理解
  6. java 中类型后面三个点的用法
  7. HTML5自定义数据属性data-*
  8. 我的docker随笔14:MySQL操作实例
  9. 一步一步搭建hibernate4+ spring+ struts2
  10. System Center Configuration Manager 文档
  11. 我的世界基岩版种子和java版种子_我的世界无限循环种子
  12. vr转换软件android版,普通视频转换成VR
  13. getch(),getche()和getchar()使用区别
  14. 万能数据库查询分析器使用技巧之(九)
  15. C语言文件——从创建到删除
  16. SuperPoint特征检测算法TrainEvaluate教程
  17. 陶哲轩实分析:数学家对于R和Q集可数性的探究
  18. css3 动画 翅膀 震动,纯css3制作煽动翅膀的蝴蝶
  19. 搜狐2009年第二季度财报电话会议实录(含我的评论)
  20. html天猫双十一倒计时代码,天猫双十一 :倒计时篇

热门文章

  1. tgp显示网络无法连接服务器,tgp显示未链接服务器
  2. patch 的生成与合入
  3. NET STOP MSSQLSERVER 数据库停止后无法使用
  4. oracle park队,Park
  5. Netty简易聊天室
  6. 高中计算机教室标语,高中教室的标语
  7. ubuntu16.04使用全记录
  8. C++抽象编程——回溯算法(1)——迷宫问题
  9. 你敢信?开发一个管理系统我只用了两天时间
  10. Airmail 添加QQ邮箱