即时协议按照是否公开可以分为私有协议(腾讯QQ)和开放协议(GTalk)。私有IM协议需要从零开始设计和搭建,时间和财力成本极高。而开放协议:
- 经过业界的长期研究和验证,在安全性、完备性容、容错性等诸多方面都有保障。
- 由于其开放的特性,业界已经有很多优秀的开源IMServer和IMClient,直接基于这些开源组件进行开发,可以在相对短的时间内快速搭建高质量的Chat基础服务。
- 开放协议一般具有较高的可扩展性,可以进行协议的扩展和改造,以适应特殊的应用场景和需求。
主流的IM协议有:
- XMPP (Extensible Messaging and Presence Protocol)
- SIMPLE (session initiation protocol for instant messaging and presence leveraging extensions)
- IMPP (Instant Messaging and Presence Protocol)。
IMPP
IMPP主要定义了必要的协议和数据格式,用来构建一个具有空间接收、发布能力的即时信息系统。到目前为止,这个组织已经出版了三个草案RFC,但主要的有两个:一个是针对站点空间和即时通讯模型的(RFC2778);另一个是针对即时通讯/空间协议需求条件的(RFC2779)。RFC2778是一个资料性质的草案,定义了所有presence和IM服务的原理。RFC2779定义了IMPP的最小需求条件。另外,这个草案还就presence服务定义了一些条款,如运行的命令、信息的格式,以及presence服务器如何把presence的状态变化通知给客户。
SIMPLE
SIMPLE计划利用SIP来发送presence信息。SIP是IETF中为终端制定的协议。SIP一般考虑用在建立语音通话中,一旦连接以后,依靠如实时协议(RTP)来进行实际上的语音发送。但SIP不仅仅能被用在语音中,也可以用于视频。SIMPLE被定义为建立一个IM进程的方法。SIMPLE在2002年夏季得到额外的信任,目前,微软和IBM都致力于在它们的即时通讯系统中实现这个协议。SIMPLE小组致力于进程模式的操作,这将提升运行效率,使基于SIP的机制能够进行会议和三方电话交谈控制,也考虑到能和未来提供的许多新特性实现兼容并提升表现能力。有了进程模式,SIMPLE使用SIP来建立一次进程,再利用SDP(进程描述协议)来实际传输IM数据。
XMPP
XMPP是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个服务器进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。
当先主流的IM协议还有很多,具体请看:Comparison of instant messaging protocols,里面包含了很多新的IM协议。特别是一些发布日期较近的,比较适合当下的无线场景。由于本文只要介绍XMPP,所以这里不做详细的介绍。
XMPP简介
XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。Jabber是一个开放源码形式组织产生的网络实时通信协议,第一个公开版本于2000年5月发行。Jabber已经由IETF XMPP协议(RFC3290)标准化。2001年成立的Jabber软件基金会于2007年更名为XMPP标准基金会。
XMPP(Extensible Messaging and Presence Protocol)
- X(eXtensible),正如上文提到的,Whatsapp后来用基于XMPP的扩展协议来替换了原生的XMPP协议。
- M(Messaging),指用户发送的消息。XMPP使用一种非常高效的推送机制来实时发送用户消息。
- P(Presence),Presence决定了XMPP实体的状态,用来告诉服务器该实体是在线、离线或者繁忙。
- P(Protocol),XMPP是一个协议,一组系统间通信的标准。
XMPP原本是为即时通讯而量身定制,但由于XML Stanza本身是XML元素,在基于XML灵活发展的特性下,使得XMPP也可以适用其它方面,已经得到了IETF的批准。XMPP与IMPP、PRIM、SIP合称四大IM协议主流:
- 即时信息和出席协议(Instant Messaging and Presence Protocol, IMPP)
- 出席和即时信息协议(Presence and Instant Messaging Protocol, PRIM)
- 针对即时信息和出席扩展的会话发起协议(Session Initiation Protocol for Instant Messaging and Presence Leveraging Extensions, SIP)
- 可扩展的消息出席协议(XMPP)
最初研发IMPP也是为了创建一种标准化的协议,但是今天,IMPP已经发展成为基本协议单元,定义所有即时通信协议应该支持的核心功能集。XMPP和SIP两种协议是架构,有助于实现IMPP协议所描述的规范。PRIM最初是基于即时通信的协议,与XMPP和SIP类似,但是己经不再使用。
在这四种协议中,XMPP是最灵活的。XMPP是一种基于XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。
XMPP系统特点
- XMPP协议是公开的,由JSF开源社区组织开发的。XMPP协议并不属于任何的机构和个人,而是属于整个社区,这一点从根本上保证了其开放性。
- XMPP协议具有良好的扩展性。在XMPP中,即时消息和到场信息都是基于XML的结构化信息,这些信息以XML节(XML Stanza)的形式在通信实体间交换。XMPP发挥了XML结构化数据的通用传输层的作用,它将出席和上下文敏感信息嵌入到XML结构化数据中,从而使数据以极高的效率传送给最合适的资源。基于XML建立起来的应用具有良好的语义完整性和扩展性。
- 分布式的网络架构。XMPP协议都是基于Client/Server架构,但是XMPP协议本身并没有这样的限制。网络的架构和电子邮件十分相似,但没有结合任何特定的网络架构,适用范围非常广泛。
- XMPP具有很好的弹性。XMPP除了可用在即时通信的应用程序,还能用在网络管理、内容供稿、协同工具、档案共享、游戏、远端系统监控等。
- 安全性。XMPP在Client-to-Server通信,和Server-to-Server通信中都使用TLS(Transport Layer Security)协议作为通信通道的加密方法,保证通信的安全。任何XMPP服务器可以独立于公众XMPP网络(例如在企业内部网络中),而使用SASL及TLS等技术更加增强了通信的安全性。如下图所示:
XMPP应用广泛,国内外已有众多大规模的应用案例。基本上,除了商业公司私有化的聊天协议,XMPP已经成为事实上的标准IM协议。
基于XMPP的方案拥有大量的Server端、Client端以及类库实现,可以快速搭建满足业务需求的应用。XMPP去中心化的设计具有天生的scale out扩展性,同时支持server-2-server通信,可以与其他XMPP服务器进行连接,实现帐号的跨域聊天。XMPP的这种特性非常适合应用于提供IM基础服务的平台。综上所诉,XMPP是以上几个协议中最完善、可扩展性最好、应用最广、组件支持最多的一个协议。因此,ChatService选用XMPP作为ChatService的IM协议。
XMPP一般会被拆分为两部分,XMPP核心协议和XMPP扩展协议。
- XMPP核心协议定义了如下服务:
- 一对一消息。即一个用户给另一个用户发送消息。
- 数据通信安全机制。通过使用STARTTLS、TLS和SASL等协议来确保消息传递的安全性。
- 在线状态和通信录。在线状态服务可以获取通信录里面的好友的状态;而通信录服务则帮助管理通信录,比如用户使用新手机登录时,可以通过该服务拉取好友列表。
- XMPP扩展协议则定义了如下服务:
- 群聊。该服务允许创建聊天室。
- 服务发现。允许客户端应用来发现服务端提供了哪些服务。
XMPP架构
XMPP的基本网络架构包含三元素:客户端、服务器、网关,具体如下图:
- 服务器:承担客户端信息记录、连接管理和信息的路由功能。
- 网关:承担着异构即时通信系统的互联互通,包括(SMS、MSN、ICQ等)。
Jabber识别符(JID)是用户登录时所使用的帐号,看起来通常像一个电子邮件地址,如:someone@example.com,前半部分是用户名,后半部分是XMPP服务器域名,两个字段以@符号区隔。
假设李雷(LiLei@A.com)想和韩梅梅(HanMeimei@B.net)通话,他们两人的帐号分别在A.com和B.net的服务器上。当李雷发送信息后,过程如下:
- 李雷的XMPP客户端将他的信息传送到com XMPP服务器。
- com XMPP服务器打开与net XMPP服务器的连接。
- net XMPP服务器将信息传送给韩梅梅,如果其当前不在线,则存储信息以待其上线后发送。
李雷和韩梅梅两人的XMPP服务器由两家不同的业者所提供,而它们彼此通信时,不需要拥有对方服务器的帐号,也不需要成为对方业者的会员,非常方便。
XMPP是一个典型的C/S架构,而不是像大多数即时通讯软件一样,使用P2P客户端到客户端的架构,也就是说在大多数情况下,当两个客户端进行通讯时,他们的消息都是通过服务器传递的(也有例外,例如在两个客户端传输文件时)。采用这种架构,主要是为了简化客户端,将大多数工作放在服务器端进行,这样,客户端的工作就比较简单,而且,当增加功能时,多数是在服务器端进行。XMPP服务的框架结构如下图所示。XMPP中定义了三个角色,XMPP客户端,XMPP服务器、网关。通信能够在这三者的任意两个之间双向发生。服务器同时承担了客户端信息记录、连接管理和信息的路由功能。网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS(短信)、MSN、ICQ等。基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在之上传输XML,工作原理是:
- 节点连接到服务器
- 服务器利用本地目录系统中的证书对其认证
- 节点指定目标地址,让服务器告知目标状态
- 服务器查找、连接并进行相互认证
- 节点之间进行交互
一个典型XMPP应用架构如下:
上图有几点需要说明:
- 客户端应用之间不会直接通信,而是通过server进行通信。
- 当server1连接的客户端A(App、PC)给server2连接的客户端B发送消息时,server1会自动将消息转发给server2,然后由server2负责将消息推送给客户端B。
用户之间发送消息时,使用Jaber id作为通信地址。一个Jaber id由user、domain、resource构成。比如下图中的http://user1@server1.com/pc就是一个Jaber id。
server之间使用jaber id里的domain来定位客户端所在的server。
user1可能会使用不同的设备,比如平板电脑、pc或者智能手机来访问同一个账户,Jaber id里的resource用来区分这些不同的设备。
XMPP客户端
XMPP系统的一个设计标准是必须支持简单的客户端。事实上,XMPP系统架构对客户端只有很少的几个限制。一个XMPP客户端必须支持的功能有:
- 通过TCP套接字与XMPP服务器进行通信
- 解析组织好的XML信息包
- 理解消息数据类型
XMPP将复杂性从客户端转移到服务器端。这使得客户端编写变得非常容易,更新系统功能也同样变得容易。XMPP客户端与服务端通过XML在TCP套接字的5222端口进行通信,而不需要客户端之间直接进行通信。
基本的XMPP客户端必须实现以下标准协议(XEP-0211):
- RFC3920核心协议Core
- RFC3921即时消息和出席协议Instant Messaging and Presence
- XEP-0030服务发现Service Discovery
- XEP-0115实体能力Entity Capabilities
XMPP服务器
XMPP服务器遵循两个主要法则:
- 监听客户端连接,并直接与客户端应用程序通信
- 与其他XMPP服务器通信
XMPP开源服务器一般被设计成模块化,由各个不同的代码包构成,这些代码包分别处理 Session 管理、用户和服务器之间的通信、服务器之间的通信、DNS(Domain Name System)转换、存储用户的个人信息和朋友名单、保留用户在下线时收到的信息、用户注册、用户的身份和权限认证、根据用户的要求过滤信息和系统记录等。另外,服务器可以通过附加服务来进行扩展,如完整的安全策略,允许服务器组件的连接或客户端选择,通向其他消息系统的网关。
基本的 XMPP 服务器必须实现以下标准协议
- RFC 3920 核心协议 Core
- RFC 3921 即时消息和出席协议 Instant Messaging and Presence
- XEP-0030 服务发现 Service Discovery
XMPP 网关
大家可能会奇怪,这里为什么需要一个网关呢。这要从 XMPP 的来源说起。1996 年 Mirabilis 公司推出了世界上第一个即时通信系统 ICQ,不到 10 年,IM 就成了最流行的应用之一,MSN、Gtalk、雅虎即时通、AIM、Adium、Pidgin 等各种软件如雨后春笋般涌现,但是这些服务之间没有统一的标准,不能互联互通,XMPP 的设计目的就是为了实现整个及时通信服务协议的互通,让 IM 成为继 WEB 和 Email 之后的互联网第三大服务。
XMPP 突出的特点是可以和其他即时通信系统交换信息和用户在线状况。由于协议不同,XMPP 和其他系统交换信息必须通过协议的转换来实现,目前几种主流即时通信协议都没有公开,所以 XMPP 服务器本身并没有实现和其他协议的转换,但它的架构允许转换的实现。实现这个特殊功能的服务端在 XMPP 架构里叫做网关(gateway)。目前,XMPP 实现了和 AIM、ICQ、IRC、MSN Massager、RSS 0.9 和 Yahoo Massager 的协议转换。由于网关的存在,XMPP 架构事实上兼容所有其他即时通信网络,这无疑大大提高了 XMPP 的灵活性和可扩展性。
XMPP 的协议
在以前及时通讯相关的指令要么用 2 进制的形式发送(比如 QQ),要么用纯文本指令加空格加参数加换行苻的方式发送(比如 MSN)。而 XMPP 传输的即时通讯指令的逻辑与以往相仿,只是协议的形式变成了 XML 格式的纯文本。这不但使得解析容易了,人也容易阅读了,方便了开发和查错。而 XMPP 的核心部分就是一个在网络上分片断发送 XML 的流协议。这个流协议是 XMPP 的即时通讯指令的传递基础,也是一个非常重要的可以被进一步利用的网络基础协议。所以可以说,XMPP 用 TCP 传的是 XML 流。
其实 XMPP 是一种很类似于 http 协议的一种数据传输协议,它的过程就如同”解包装–〉包装”的过程,用户只需要明白它接受的类型,并理解它返回的类型,就可以很好的利用 xmpp 来进行数据通讯。
XML 流传输协议
XMPP 的核心 XML 流传输协议的定义使得 XMPP 能够在一个比以往网络通信协议更规范的平台上。借助于 XML 易于解析和阅读的特性,使得 XMPP 的协议能够非常漂亮。XMPP 的即时通讯扩展应用部分是根据 IETF 在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如 AIM,QQ 等有功能完整,完善等先进性。XMPP 的扩展协议 Jingle 使得其支持语音和视频。XMPP 的官方文档时 RFC 392。
客户端开始和 XMPP 服务器会话,会打开一个长时间的 TCP 连接,然后和服务器协商一个流。一旦你和你的服务器建立了一个 XML 流,你和你的服务器可以通过流交换三个特殊的 XML 片段,这些片段称为 XML 节。是 XML 中最有意义的基本单元,而且一旦你已建立一个 XML 流,你可以通过流发送无数个节。
- 客户端与服务器通信的过程中,服务器必须允许客户端共享一个 TCP 连接来传输 XML 节,包括从客户端传到服务器和从服务器传到客户端。
- 服务器到服务器的通信过程中,服务器必须用一个 TCP 连接向对方发送 XML 节,另一个 TCP 连接(由对方初始化)接受对方的 XML 节,一共两个 TCP 连接。
举例看看所谓的 XML 流是什么样子的?
- 客户端:<?xml version=’1.0′?><stream:stream to=’example_com’ xmlns=’jabber:client’ xmlns:stream=’http_etherx_jabber_org/streams’ version=’1.0′>
- 服务器:<?xml version=’1.0′?><stream:stream from=’example_com’ id=’someid’ xmlns=’jabber:client’ xmlns:stream=’http_etherx_jabber_org/streams’ version=’1.0′>
- …其他通信…
- 客户端:<message from=’juliet_example_com’ to=’romeo_example_net’ xml:lang=’en’>
- 客户端:<body>Art thou not Romeo, and a Montague?</body>
- 客户端:</message>
- 服务器:<message from=’romeo_example_net’ to=’juliet_example_com’ xml:lang=’en’>
- 服务器:<body>Neither, fair saint, if either thee dislike.</body>
- 服务器:</message>
- 客户端:</stream:stream>
- 服务器:</stream:stream>
以文档的观点来看,客户端或服务器发送的所有 XML 文本连缀在一起,从 <stream> 到 </stream> 构成了一个完整的 XML 文档。其中的 stream 标签就是所谓的 XML Stream。在 <stream> 与 </stream> 中间的那些 <message>…</message> 这样的 XML 元素就是所谓的 XML Stanza(XML 节)。XMPP 核心协议通信的基本模式就是先建立一个 stream,然后协商一堆安全之类的东西,中间通信过程就是客户端发送 XML Stanza,一个接一个的。服务器根据客户端发送的信息以及程序的逻辑,发送 XML Stanza 给客户端。但是这个过程并不是一问一答的,任何时候都有可能从一方发信给另外一方。通信的最后阶段是 </stream> 关闭流,关闭 TCP/IP 连接。
XMPP 地址格式
因为 xmpp 通讯实在网络上,所以每个 xmpp 实体都需要一个地址,称为 Jabber ID (JID)。一个合法的 JID 包括一组排列好的元素,包括域名(domain identifier), 节点名(node identifier), 和资源名(resource identifier)。node@domain/resource 这种结构,最常用来标识一个即时消息用户,这个用户所连接的服务器,以及这个用户用于连接对资源。一个 JID 的每一个合法部分的长度不能超过 1023 字节。
- 域名是一个主要的 ID,并且是 JID 中唯一必须的元素。(一个纯粹的域名也是一个合法的 JID)。它通常代表网络的网关或者”主”服务器,其他实体通过连接它来实现 xml 转发和数据管理功能。然而,由一个域名标识引用的实体,并非总是一个服务器,也可能是一个服务器子域名地址。
- 节点名是一个可选的第二 ID,放在域名之前并用符号”@”分开。它通常表示一个向服务器或网关请求和使用网络服务的实体(比如一个客户端),当然也可能表示其他实体(如聊天室中的一个房间)。
- 资源名是一个可选的第三 ID,它放在域名后面并由”/”分开,资源名可以跟在 node@domain 后面也可以跟在后面。它通常表示一个特定的会话连接,或者附属于某个节点 ID 实体相关实体的对象。
XMPP 消息格式
在 RFC 3920 XMPP Core 中定义了两个基础概念,XML Stream 和 XML Stanza,XML Stream 是两个节点之间进行数据交换的容器,它定义了顶层的 XML 节点;XML Stanza 则定义了实体消息的具体语义单元,在 XMPP 中定义了 3 个顶层消息:Message、Presence 和 IQ。
<Message>
Message 是一种基本推送消息方法,它不要求响应。主要用于 IM、groupChat、alert 和 notification 之类的应用中。
主要属性如下:
- type 属性,它主要有 5 种类型:
- normal:这种消息类型立即被传递或被服务器离线存储,并被客户端以任何聊天或群聊对话外的独立消息来处理。这个类型是默认值。类似于email,主要特点是不要求响应。
- chat:chat消息类型在”聊天会话”中通常在相对短的时间内以突发消息发送。即时消息客户端在一对一的对话界面中显示这些信息。类似于qq里的好友即时聊天,主要特点是实时通讯。
- groupchat:XMPP服务器通常将路由类型为groupchat的消息路由到一个拥有多个聊天室的专门组件或模块,并且这个组件产生一个向外的消息给房间的每一个人。类似于聊天室里的群聊。
- headline:headline消息通常不被离线存储,因为他们是临时性的。另外,XMPP服务器经常传递headline类型的消息到所有和账户关联的在线设备(至少priority值为非负的)。用于发送alert和notification。
- error:error类型的消息是为了应答先前发送的消息而被发送出去的,以指示和先前消息有关的错误发送(接收人不出席,此时不能发送消息等)。如果发送message出错,发现错误的实体会用这个类别来通知发送者出错了。
- to属性:标识消息的接收方。
- from属性:指发送方的名字或标示。为防止地址外泄,这个地址通常由发送者的server填写,而不是发送者。
- 载荷(payload):此元素包含了要提交给目标用户的信息。例如body,subject,thread
消息结构如下:
<message from="abc@jabber.org/contact" to="def@jabber.org/contact" type="chat"> <body>hello</body> </message>
在聊天中加入不知道聊天对象的状态会让人误以为对方已经离线或不想搭理人,未免让人心中有所失落。比如,你和你的女友聊天,你问:”亲爱的,今天都干啥了”,你的女友很兴奋,她有很多话要告诉你,由于打字的时间太久让你觉得她不爱你….好嘛,这样的聊天是不是很虐心….然后,办法还是有的,你需要在你的IM系统中得到聊天状态的通告,定义聊天状态通告[XEP-0085]
聊天状态:
- Starting:人开始一个对话,但是你还没有参与进来
- Active:正参与在对话中,当前你没有组织你的消息,而是在关注
- Composing:正在组织一个消息
- Paused:开始组织一个消息,但由于某个原因而停止组织消息
- Inactive:一段时间里没有参与这个对话
- Gone:参与的这个对话已经结束
示例:
<message from="abc@jabber.org/contact" to="def@jabber.org/contact" type="chat"> <body>hello</body> <starting xmlns="http://jabber.org/protocol/chatstates"/> </message>
<Presence>
presence用来表明用户的状态,如:online、away、dnd(请勿打扰)等。当改变自己的状态时,就会在stream的上下文中插入一个Presence元素,来表明自身的状态。要想接受presence消息,必须经过一个叫做presence subscription的授权过程。
属性:
- type属性,非必须。有以下类别
- subscribe:订阅其他用户的状态
- probe:请求获取其他用户的状态
- unavailable:不可用,离线(offline)状态
- to属性:标识消息的接收方。
- from属性:指发送方的名字或标示。
- 载荷(payload):
- show:
- chat:聊天中
- away:暂时离开
- xa:eXtend Away,长时间离开
- dnd:勿打扰
- status:格式自由,可阅读的文本。也叫做rich presence或者extended presence,常用来表示用户当前心情,活动,听的歌曲,看的视频,所在的聊天室,访问的网页,玩的游戏等等。
- priority:范围-128~127。高优先级的resource能接受发送到bare JID的消息,低优先级的resource不能。优先级为负数的resource不能收到发送到bare JID的消息。
消息结构如下:
<presence from="abc@jabber.org/contact" to="def@jabber.org/contact"> <status>online</status> </presence>
<IQ>(Info/Query)
一种请求/响应机制,从一个实体从发送请求,另外一个实体接受请求,并进行响应。例如,client在stream的上下文中插入一个元素,向Server请求得到自己的好友列表,Server返回一个,里面是请求的结果。
主要的属性是type。发送IQ节的实体必须总是接收一个回复(通常由目的接收者或接受者的服务器产生)。请求和应答通过使用id属性跟踪,id属性由请求实体生成,并被包含在应答的实体中。
- Get:请求实体信息,例如请求注册一个账户(类似于HTTP GET)。
- Set:请求实体提供一些信息或作出一个请求(类似于HTTP POST或PUT)。
- Result:应答实体返回get操作的结果(例如一个实体必须提供信息用来注册账户),或者确认一个set请求(类似于一个HTTP 200状态码)。
- Error:应答实体或一个中间实体,例如XMPP服务器,通知请求实体它不能处理get或set请求(例如,因为请求的格式不正确,请求实体无权执行该操作等)。早期在HTTP中使用的数字错误代码已被可扩展错误条件的XML元素取代。
消息结构如下:
<iq from="abc@jabber.org/contact" id="id11" type="result"></iq>
说到这里,就不得不说XMPP一只被吐槽的缺点:由于使用XML,网络流量的70%都消耗在XMPP协议层了。基于此WhatsApp在此基础上改进了XMPP协议。
XMPP协议的组成
主要的XMPP协议范本及当今应用很广的XMPP扩展:
- RFC3920 XMPP:核心。定义了XMPP协议框架下应用的网络架构,引入了XML Stream(XML流)与XML Stanza(XML节),并规定XMPP协议在通信过程中使用的XML标签。使用XML标签从根本上说是协议开放性与扩展性的需要。此外,在通信的安全方面,把TLS安全传输机制与SASL认证机制引入到内核,与XMPP进行无缝的连接,为协议的安全性、可靠性奠定了基础。Core文档还规定了错误的定义及处理、XML的使用规范、JID(Jabber Identifier,Jabber标识符)的定义、命名规范等等。所以这是所有基于XMPP协议的应用都必需支持的文档。
- XEP-0030 服务搜索。一个强大的用来测定XMPP网络中的其它实体所支持特性的协议。
- XEP-0115 实体性能。XEP-0030的一个通过即时出席的定制,可以实时改变交变广告功能。
- XEP-0045 多人聊天。一组定义参与和管理多用户聊天室的协议,类似于Internet的Relay Chat,具有很高的安全性。
- XEP-0096 文件传输。定义了从一个XMPP实体到另一个的文件传输。
- XEP-0124 HTTP绑定。将XMPP绑定到HTTP而不是TCP,主要用于不能够持久的维持与服务器TCP连接的设备。
- XEP-0166 Jingle。规定了多媒体通信协商的整体架构。
- XEP-0167 Jingle Audio Content Description Format。定义了从一个XMPP实体到另一个的语音传输过程。
- XEP-0176 Jingle ICE(Interactive Connectivity Establishment)Transport。ICE传输机制,文件解决了如何让防火墙或是NAT(Network Address Translation)保护下的实体建立连接的问题。
- XEP-0177 Jingle Raw UDP Transport。纯UDP传输机制,文件讲述了如何在没有防火墙且在同一网络下建立连接的。
- XEP-0180 Jingle Video Content Description Format。定义了从一个XMPP实体到另一个的视频传输过程。
- XEP-0181 Jingle DTMF(Dual Tone Multi-Frequency)。
- XEP-0183 Jingle Telepathy Transport Method。
RFC3921:用户成功登陆到服务器之后,发布更新自己的在线好友管理、发送即时聊天消息等业务。所有的这些业务都是通过三种基本的XML节来完成的:IQ Stanza(IQ节), Presence Stanza(Presence节), Message Stanza(Message节)。RFC3921还对阻塞策略进行了定义,定义是多种阻塞方式。可以说,RFC3921是RFC3920的充分补充。两个文档结合起来,就形成了一个基本的即时通信协议平台,在这个平台上可以开发出各种各样的应用。
XMPP服务器的选型
XMPP Server方案
XMPP Server拥有众多的实现方案:
其中,较为成熟,且使用广泛的是ejabberd, jabberd 2.x, Openfire, Tigase:
- Jabberd 2.x使用C语言实现,但是,存在着数据库事务的滥用、内存泄露、不一致的非阻塞设计等问题,最重要的是该server已经很长时间没有人维护;因此,chesspark在使用jabberd 2.x三年后,转用ejabberd。无独有偶,org也在2010年淘汰Jabberd, 转为使用
- Openfire以及Tigase都是基于JAVA的解决方案。但是Openfire单机并发很有限,集群方案不成熟,代码古老而缺乏及时更新,因此不适合应用在生产环境中。但Tigase其集群方案实现复杂,单节点容量有限.
从编程语言角度看,主流的XMPP Server主要是JAVA和Erlang。JAVA语言的优势是类库完备,容易招人。Erlang的优势是hot code swap, live console, 高并发。ejabberd与Openfire/Tigase比较而言,最大的优势是相对优雅的集群方案以及更高的并发性能。在没有语言偏向性的情况下,一般在开发初期都会选用ejabberd, 如米聊。
从各种XMPP Server支持的特性看,ejabberd是对XMPP协议支持最好、实现最为全面的server。
从开源协议看,Tigase采用AGPL开源协议,因此只要有代码修改,就必须对代码进行开源。Openfire采用Apache开源协议,修改代码后可以闭源。ejabberd采用GPLv2开源协议,如果只在服务端提供服务,而不进行代码二次分发,修改代码以后也可以「闭源」。
综合以上各因素,ChatService采用ejabberd作为初期的XMPP Server。
ejabberd
- ejabberd是一个用Erlang/OTP语言写的自由和开源的即时消息服务器
- ejabberd是跨平台, 分布式, 容错, 并基于开放标准的实时通讯系统
- ejabberd的设计坚若磐石,是一个功能服丰富的XMPP服务器
- ejabberd同时适合小规模布署和超大规模布署, 无论它们是否需要可伸缩性
关键功能:
- 跨平台的: ejabberd可以运行在Microsoft Windows和Unix派生系统,例如Linux, FreeBSD和
- 分布式的: 你可以在一个集群的机器上运行ejabberd,并且所有的机器都服务于同一个或一些Jabbe域. 当你需要更大容量的时候,你可以简单地增加一个廉价节点到你的集群里. 因此, 你不需要买一个昂贵的高端机器来支持上万个并发用户.
- 容错: 你可以布署一个ejabberd集群,这样一个正常运行的服务的所有必需信息将被复制到所有节点. 这意味着如果其中一个节点崩溃了, 其他节点将无中断的继续运行. 另外, 也可以‘不停机’增加或更换节点.
- 易于管理: ejabberd建立于开源的所以你不需要安装外部服数据库, 外部web服务器, 除此以外因为每个东西都已经包含在里面, 并且处于开箱可用状态. 其他管理上的好处包括:
- 详尽的文档
- 便捷的安装程序,包括在Linux, Mac OS X, 以及Windows系统下
- Web管理
- 共享名册组
- 命令行管理工具
- 可外部集成的验证机制
- 发送匿名消息的能力
- 国际化: ejabberd领导国际化. 非常适合全球化. 相关功能包括:
- 翻译成25种语言
- 支持IDNA
- 开放标准: ejabberd是的一个完全支持XMPP标准的开源Jabber服务器.
- 完全兼容
- 基于XML的协议
- 支持的协议很多
额外功能:
- 模块化
- 只装在你想要的模块
- 在你自己的自定义模块扩展
- 安全性
- 支持c2s和s2s连接的SASL
- 支持s2s连接的STARTTLS
- 可通过HTTPS安全访问的Web管理
- 数据库
- 快速布署的内部数据库(Mnesia)
- 原生的MySQL支持
- 原生的PostgreSQL支持
- 支持ODBC数据存储
- 支持Microsoft SQL Server
- 验证
- 内部验证
- PAM, LDAP
- 外部验证脚本
- 其他
- 支持虚拟主机
- XML流压缩(XEP-0138)
- 统计(XEP-0039)
- 支持IPv6的c2s和s2s连接
- 支持集群和HTML日志的多用户聊天模块
- 基于用户vCards的用户目录
- 支持基于PubSub的个人事件的发行-订阅组件
- 支持web客户端: HTTP轮询和HTTP绑定(BOSH)服务
- IRC网关
- 组件支持: 安装特定网关之后和外部网络的接口,如AIM, ICQ和
OpenFire
我们要了解Openfire,首先要了解XMPP协议,因为Openfire是用Java语言编写的,基于XMPP协议、开源的实时协作的服务器。Openfire具有跨平台的能力,Openfire与客户端采用的是C/S架构,一个服务器要负责为连接在其上的客户端提供服务。Openfire客户端有spark, pidgin, MirandaIM, iChat等,用户如果自己开发客户端,可以采用遵循GPL的开源Client端API–Smack。Openfire服务器端支持插件开发,如果开发者需要添加新的服务,可以开发出自己的插件后,安装至服务器,就可以提供服务,如查找联系人服务就是以插件的形式提供的。
OpenFire是采用Java开发的基于XMPP(Jabber)协议,开源实时协作(RTC)服务器。Smack是用Java编写的XMPP客户端代码库,是spark的核心,开源界总是有许多有趣的东东,这三个合起来就是一个完整的XMPP IM实现。
- OpenFire ——服务器端
- Spark——客户端
- Smack——XMPP传输协议的实现
三者都是基于Java语言的实现,其中XMPP是一个协议,协议是需要实现的,Smack起到的就是这样的一个作用。
OpenFire 是基于XMPP协议的IM的服务器端的实现,虽然当两个用户连接后,可以通过点对点的方式来发送消息,但是用户还是需要连接到服务器来获取一些连接信息和通信信息的,所以服务器端是必须要实现的。
Spark提供了客户端一个基本的实现,并提出了一个很好的插件架构,这对于开发者来说不能不说是一个福音。我强烈建议基于插件方式来实现你新增加的功能,而不是去改它的源代码,这样有利于你项目架构,把原始项目的影响降到最低。
Smack是一个XMPP协议的Java实现,提供一套可扩展的API,不过有些时候,你还是不得不使用自己定制发送的XML文件内容的方式来实现自己的功能
三者之间的关系:
从上图可知,Client端和Server端都可以通过插件的方式来进行扩展,Smack是二者传递数据的媒介。
Openfire服务器容量
单台Openfire服务器支持3K以上的容量应该是没有问题,但更多的则需要测试。开源版本的不支持Clustering,但我们可以采用Openfire+ConnectionManager来扩充服务器容量,有网友测试,在1台8G内存的Openfire服务器上,如果配置好ConnectionManager,可以支持到30万用户。我们可以来算算Openfire+ConnectionManager服务器的大概容量。假设一个ConnectionManager与Openfire之间各占用20连接,每个Openfire服务器可支持5000个连接,每个ConnectionManager可支持3000个连接,如果我们配置4个ConnectionManager,则我们可算出Openfire服务器的容量:1*5000-4*20+4*3000-4*20=16840。我们做了一个测试,在虚拟机上运行Openfire3.7.0,可以同时登录2W个用户,在加上Clustering之后,只有1W6了。
参考链接: