Smack入门与实战:仿写微光同步看电影聊天室

1,763 阅读40分钟

首先说下本课程要仿写的App吧,微光,这个我应该不用介绍了吧, 如您不了解,也没关系,我这里并不是给它做推广,因为我目前也在运营一款类似的App,我个人当然不会给我的竞争对手做广告,但是毕竟人家做的比较完善,人气也当然比我独立开发的App要高,所以我还是选它来作为仿写的对象吧

微光聊天室截图

上图是我从微光截的图,大概我们就是要仿写成这样,当然不会仿写的那么全面,只做个简单的聊天室发送与接收文字图片消息,同时观看视频电影并且实现进度同步即可

介绍完本系列课程的目的后,我来给我自己做个介绍吧,本人做安卓开发快5年了,私下里独立开发过一款类似微光的社交App,上架过小米,应用宝,酷安,魅族,百度手机助手,也成功申请到小米,应用宝的首发,上完应用宝首发后,酷安貌似会自动拉取到应用信息,帮我上架到它们平台,截至目前,注册用户已达5万多,基本都来自于酷安,其它平台个人觉得真不如酷安

因为本系列课程主要涉及即时通讯,所以有关后台接口设计以及环境搭建,我不会多讲,必要的话可能还是要涉及一点后端知识,因为我后端采用的是Nginx,Php,Mysql,所以Php,Mysql我在接下来的课程中会稍带提点,但不会多讲就是了,作为一款即时通讯社交软件,当然少不了通讯协议,笔者经过多方挑选后,最终选择了本节课程的主角:XMPP,那我们就来扒一扒XMPP协议吧!

在介绍XMPP协议之前我想先带大家回顾下计算机网络相关的知识,大学学习网络基础的时候老师讲过,网络由下往上分为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,那我们就从这几个结构层次上来划分下常见的网络协议(计算机中为进行数据交换而建立的标准规则):

IP协议

对应网络层,这个就是大家常说的ip地址,不做过多介绍

TCP协议

对应传输层,是一种面向连接的传输控制协议(transform contorl protocol),必须要和服务器交互,具有高安全性,可靠性,需要和服务器进行三次握手,能根据具体网络拥堵情况进行延时,TCP支持的应用协议有:

Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传输协议)

实际应用例如:

MSN文件传输

UDP协议

对应传输层,一种面向无连接的用户数据报服务(user data protocol),不需要和服务器也能交互,只需要知道ip和监听端口,不需要链接没有目的的socket,只是将数据报投递出去,不管接收方是否成功接收到,就像短信,QQ,因此是一种不可靠的传输,可能会造成数据丢包,但由于这些特征,传输效率要优于TCP,UDP支持的应用协议有:

NFS(网络文件系统)、SNMP(简单网络管理系统)、DNS(主域名称系统)、TFTP(通用文件传输协议)等

实际应用例如:

QQ文件传输(所以传输速度要高于上面采用TCP协议的MSN),屏幕共享,视频直播

TCP/IP

对应网络层传输层,传输控制协议/网际协议(Transmission Control Protocol/Internet Protocol)是指能够在多个不同网络间实现信息传输的协议簇,TCP/IP协议不仅仅指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。

Socket

原意:“插座”,俗称“套接字”,Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口,socket就像是TCP与UDP的外接API。有两种连接操作方式,面向连接的(TCP)和面向无连接的(UDP)。使用UDP无需要指定一个socket目的地,而是用TCP必须要指定一个socket目的地,需要进行预链接,否则连接不了,正如它的英文原意“插座”一样,就像是主机上的多孔插座一样,

一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。(以上出自360百科)

socket原理图

实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议,关于TCP/IP和Socket之间的关系,网上有段通俗易懂的说法

TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”

在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议簇隐藏在Socket接口后面,Socket作为一套Api接口,是对TCP/IP协议的抽象,如果非要给它划分下的话,我觉得应该属于传输层吧,实际应用例如:

QQ以及各种即时通讯软件

WebSocket协议

对应网络层****,HTML5出的东西(协议),提供的一种在单个 TCP 连接上进行全双工通讯的协议,和Http协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充,可以通过这样一张图理解

WebSocket和Http的关系

有交集,但是并不是全部。相对于HTTP这种非持久的协议来说,Websocket是一个持久化的协议,从通讯方向上来说,HTTP是单向的,而Socket是双向的,与HTTP不同,它以**ws://wss://**开头。它是一个有状态协议,这意味着客户端和服务器之间的连接将保持活动状态,直到被任何一方(客户端或服务器)终止。在通过客户端和服务器中的任何一方关闭连接之后,连接将从两端终止。那么WebSocket和Socket又有什么联系呢?

是的,没有任何关系,就像是雷锋和雷峰塔,Java和JavaScript一样,虽然字面上很像,但是就是没有半点关系

可以这么说:

  • 命名方面,Socket是一个深入人心的概念,WebSocket借用了这一概念;

  • 使用方面,完全两个东西。

WebSocket就和下边要讲的HTTP协议一样,都属于应用层,而且都是一样基于TCP的,都是可靠性传输协议,HTTP和Socket是什么样的关系,WebSocket和Socket就是什么样的关系

实际应用例如:

即时Web应用程序: 例如在交易网站或比特币交易中,这是最不稳定的事情,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。

游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在WebSocket游戏应用程序中非常有帮助。

聊天应用程序:聊天应用程序仅使用WebSocket建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的WebSocket连接,用于发送和接收消息以及一对一的消息传输。

HTTP协议

对应应用层,HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用,HTTP是单向的,客户端发送请求,服务器发送响应。举例来说,当客户端向服务器发送请求时,该请求以HTTP或HTTPS的形式发送,在接收到请求后,服务器会将响应发送给客户端。每个请求都与一个对应的响应相关联,在发送响应后客户端与服务器的连接会被关闭。每个HTTP或HTTPS请求每次都会新建与服务器的连接,并且在获得响应后,连接将自行终止。HTTP是在TCP之上运行的无状态协议,简单的说,TCP就是单纯建立连接,不涉及任何我们需要请求的实际数据,简单的传输。http是用来收发数据,即实际应用上来的。

实际应用:

浏览器打开网页,公司OA服务,互联网服务

总结

IP是高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口 TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,HTTP提供了封装或者显示数据的具体形式 Socket是发动机,提供了网络通信的能力。

上述网络协议之间的关系也可以用下面这张图来概括

常用网络协议基于网络结构的划分

我们该选用什么网络传输协议(TCP/UDP/HTTP)?

udp协议虽然实时性更好,但是如何处理安全可靠的传输并且处理不同客户端之间的消息交互是个难题,实现起来过于复杂. 目前大部分IM架构都不采用UDP来实现

HTTP更不用谈了,虽然HTTP也可以实现长连接,在响应头加入这个头部字段:Connection:keep-alive,但是Keep-Alive不会永久保持连接,它有一个保持时间

TCP: 维护长连接,保证消息的实时性, 我们的目的刚好就是及时收发消息,那啥都不用说了

好,选择完网络协议后,现在咱们来聊聊IM即时通讯协议吧,开始之前,咱们先来了解下什么是即时通讯

即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷;服务提供商也提供了越来越丰富的通讯服务功能,常见的即时通讯工具有QQ、微信等。然而如果要搭建一个即时通讯系统,离不开即时通讯协议。总的来说,即时通讯协议就是即时通讯系统在通讯时所需要遵循的规范,它定义了一些标准化的通讯数据格式和网络架构等。

常用的即时通讯协议

  • 空间和即时信息协议(PRIM)

  • 即时信息和空间协议(IMPP)

  • 针对即时通讯和空间平衡扩充的进程开始协议(SIP)

  • 可扩展通讯和表示协议(XMPP)

  • 私有协议(自定义协议):为了更满足特定的需求和安全性,有些开发者会重新定义一套协议,这个成本是非常巨大的,一般是大厂干的事(例如腾讯),大部分主流IM APP都是使用私有协议,一个被良好设计的私有协议一般有如下优点:高效,节约流量(一般使用二进制协议),安全性高,难以破解。

抛开私有协议不谈(毕竟难度系数较高),我们来说说前四种

空间和即时信息协议(PRIM)

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

从2001年起,该项目无任何进展,大家就请忽略吧

即时信息和空间协议(IMPP)

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

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

针对即时通讯和空间平衡扩充的进程开始协议(SIP)

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

SIMPLE(The Session Initiation Protocol for Instant Messaging and Presence Leveraging Extensions)协议是由IETF定义的基于SIP的一个即时消息(IM)和空间协议套。与现今广泛使用的软件实现即时通信与空间协议相比,SIMPLE是类似于XMPP的公开标准。SIMPLE使用SIP协议发送presence信息。SIP是IETF为终端定制的一种协议,其一般用于建立语音通话中,一旦建立连接后,使用实时协议(RTP)进行实际上的语音发送。但SIP不仅可以用在语音上,也可以用在视频上。SIMPLE被定义为一个建立IM进程的方法。

正如上边所说,PRIM与IMPP、SIMPLE类似,都已经不再使用了。

可扩展通讯和表示协议(XMPP)

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

咦,等等,你还没说它的缺点呢!

先别急,我并不是说有意要扬长避短,它的缺点还是有的,而且还不少,那咱们现在就来说说

XMPP的缺点

  • 太多冗余信息

  • 移动端开销大,也就是耗费流量多(毕竟承载了本来可以省略的东西,但是毕竟可读性也高,而且易解析,开发和查错,不像早期QQ采用的二进制,那是人该看的玩意么)

  • XMPP的生态系统不算大

  • 正是因为生态系统不够完善,所以带来的问题就是实际使用时有大量天坑。

好,看完以上缺点,成功把各位看官劝退了

桥豆麻袋,虽然XMPP缺点是很多,但是对于我们入门即时通讯来说,确实是个好的选择,一个是因为相对其它协议来说,相对门槛低些,而且也由于XML的特性,也使得它的扩展性极强,总的来说,优势如下:

  • 开放:XMPP协议是自由、开放、公开的,并且易于了解。而且在客户端、服务器、组件、源码库等方面,都已经各自有多种实现。

  • 标准:互联网工程工作小组(IETF)已经将Jabber的核心XML流协议以XMPP命名,正式列为认可的实时通信及Presence技术。而XMPP的技术规格已被定义在RFC 3920及RFC 3921。任何IM供应商在遵循XMPP协议下,都可与Google Talk实现连接。

  • 证实可用:第一个Jabber(现在XMPP)技术是Jeremie Miller在1998年开发的,现在已经相当稳定;数以百计的开发者为XMPP技术而努力。今日的互联网上有数以万计的XMPP服务器运作着,并有数以百万计的人们使用XMPP即时通讯软件。

  • 分布式:XMPP网络的架构和电子邮件十分相像;XMPP核心协议通信方式是先创建一个stream,XMPP以TCP传递XML数据流,没有中央主服务器。任何人都可以运行自己的XMPP服务器,使个人及组织能够掌控他们的实时传讯体验。

  • 安全:任何XMPP协议的服务器可以独立于公众XMPP网络(例如在企业内部网络中),而使用SASL及TLS等技术的可靠安全性,已自带于核心XMPP技术规格中。

  • 可扩展:XML命名空间的威力可使任何人在核心协议的基础上建造定制化的功能;为了维持通透性,常见的扩展有支持音视频传输的扩展协议Jingle。

  • 弹性佳:XMPP除了可用在实时通信的应用程序,还能用在网络管理、内容供稿、协同工具、文件共享、游戏、远程系统监控等。

  • 多样性:用XMPP协议来建造及布署实时应用程序及服务的公司及开放源代码计划分布在各种领域;用XMPP技术开发软件,资源及支持的来源是多样的,使得使你不会陷于被“绑架”的困境。

虽然上述优点和前面谈到的缺点有一些矛盾的地方,但是不得不承认,XMPP是值得我们去做开发使用的,我的独立开发的应用正是使用了XMPP协议,目前没有什么出现大的问题,还是比较稳定的,并且据说官方正在制定新的协议规范,旨在解决上述例如数据冗余等缺点,而且纵观他们的发展历史,每年都有举办XMPP峰会,这样看来,各位大可不用担心哪天XMPP协议被抛弃

对了,除了前面所说的四种通信协议外,还有两种协议我没提到

Protobuf协议

是google开源的一个序列化框架,类似xml,json,最大的特点是基于二进制,比传统的XML表示同样一段内容要短小得多

优点:非常小、非常快、非常简单,一条消息数据用Protobuf序列化后的大小是JSON的1/10、XML格式的1/20、是二进制序列化的1/10。

缺点:不能表示复杂的数据结构

MQTT协议

轻量级的、基于代理的“发布/订阅”模式的消息传输协议,它的特点在于协议简单,流量少,但是它并不是一个专门为IM设计的协议,多使用于推送.,物联网领域,需要自己在业务上实现群,好友相关等等

我不建议各位使用MQTT,因为MQTT并不是专门为即时通讯领域而设计的,而且需要我们自己去实现各种复杂业务,所以综合来看,还是选择相对成熟一点的XMPP协议吧

既然选择了XMPP,那我们就来详细了解下

什么是XMPP?

XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据,基于可扩展标记语言(XML)的协议,它继承了在XML环境中灵活的发展性,可用于实时消息传递、呈现和请求/响应业务。XMPP使用TCP传输XML数据流,属于应用层协议,而非传输层协议。

我在XMPP的官网(xmpp.org)上找到了如下两个角度的定义

哲学概述

一种开放,安全的技术,可用于即时消息传递以及更多其他功能。

一个由终极用户和开发人员组成的友好社区,他们重视交流的自由。

IM服务的分散网络,您的消息不受AOL,Microsoft或Yahoo等大公司的监视。您甚至可以运行自己的服务器!

由IETF和XMPP标准基金会发布的一套用于实时通信的稳定标准。

即时消息应有的方式!

换句话说,XMPP不是封闭的,不安全的,不友好的,集中的,专有的即时消息服务,例如AIM,ICQ,MSN或Yahoo。

技术概述

XMPP是一种开放,安全,无垃圾邮件,无广告的去中心化替代品,可替代消费者IM孤岛,例如AOL Instant Messenger,ICQ,Windows Live Messenger和Yahoo Instant Messenger。在幕后,XMPP是一组流XML协议,这些协议使Internet上的任何两个实体都可以实时地交换消息,状态和其他结构化信息。(PS:机翻,翻译的不好,大概明白意思即可)

总结

XMPP其实就是一套为即时通讯系统指定的一系列规范,大家如果做即时通讯的话都能够遵循这套标准规范来做的话,也就是基于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协议也是从一个无名小辈慢慢发展到被IETF核心规范化,并且不断发展至如今大规模地使用,补充一点,我们可能会在有些地方看到XMPP又叫Jabber协议,因为XMPP的前身就是由JSF发布的Jabber协议,后来改的名,可以这样说,XMPP协议是基于Jabber协议发展而来的

了解完XMPP的历史后,我们还应该了解它是怎样实现的网络数据通讯,那么XMPP的工作原理究竟是什么呢?

XMPP的工作原理

client--server--client(server可以当作是一个中转站),具体工作流程如下

  • Client1(客户端1)连接到服务器;

  • 服务器利用本地目录系统中的证书对其认证;

  • Client1(客户端1)指定目标Client2(客户端2)地址,让服务器告知目标状态;

  • 服务器查找、连接并进行相互认证;

  • Client(客户端)之间(Client1与Client2)进行交互。

  • 如果再复杂点,要和其他通讯系统(例如SMS短信服务,MSN,SMTP,ICQ等)交互信息,就需要配置xmpp网关。

xmpp协议通讯原理

总结

XMPP协议采用的是C/S架构,和一般的P2P客户端到客户端的架构不同,他是客户端到服务器再到客户端的一种架构,服务器用来处理消息和路由,这么做的好处是将大部分工作都放在服务器端完成,不需要前端开发人员做太多的事情,也不限于用户设备的配置,只要服务器配置高,性能好,就足够了,大概的工作流程,可以用如下通俗的语言来叙述:

客户端A对服务器说:“喂喂喂,服务器在吗,我要和你连接”

服务器回答说:”在的,稍等,我先用我本地的证书给你身份进行个认证.......好了,认证完了,确认你是A,那么请你告诉我你要和谁发起聊天吧“

客户端A:"我要和B聊天"

服务器:”好的,那么请稍后,我来查下B当前的状态.......B当前是在线的,我这就给你转接到B......好了,你现在可以和B通话了“
客户端A:”喂喂喂,B,你在吗,能听到我说话“

客户端B:”在的,能听到“

大概就是这么个流程,如果复杂点,还需要和在国外的C(基于其它协议的即时通讯系统,如SMS短信,MSN,Google Talk,ICQ)通信,那么就需要设置网关,配置好网关后,就可以和远在国外的C进行通话

XMPP协议的基本组成

XMPP协议定义了三个角色,这三者共同组成了XMPP

  • 服务器:同时承担了客户端信息记录,连接管理和信息的路由功能。

  • 网关:一个特殊用途的服务器端的服务,主要功能是把xmpp翻译成外部消息系统,并把返回的消息翻译成xmpp。基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在其之上传输XML。

  • 客户端:通过TCP连接直接连接到服务器,并通过xmpp获得由服务器以及联合服务器所提供的全部功能。多个不同的客户端可以同时登陆并且并发的连接到一个服务器,每个不同资源的客户端通过xmpp地址的资源标识符(下文会说)来区分。

总结

通过上面讲的XMPP的通讯原理,我们应该很容易这三者各自承担的工作了吧,另外需要补充的一点,就是这三者之间任意两个都可以实现互相通信,也就是服务器可以和客户端互相通信,客户端也可以和网关互相通信,服务器也可以和网关相互通信,相当于形成了一个闭环

综上,我们差不多对XMPP已经有了一番了解,但是我们仍然不知道XMPP到底做了哪些规范,具体的写法究竟是什么样的呢?下面就来说说XMPP定义的消息格式以及地址格式

XMPP寻址(也就是实体地址格式,可以理解为用户id)

XMPP网络上的每个实体都有一个或多个地址(称为JID,jabber identifier)

等等,啥叫实体?对于XMPP来说,实体通常是指客户端和服务器,但是其也允许客户端与客户端或服务器端与服务器端的通信,这里我们只讨论客户端和服务器,所以大家就把实体当作是通讯的两端,一端是服务器,一端是客户端,为了识别每个客户端或者服务器,我们给每个实体定义了一个或多个地址,简称JID,这个地址是什么样子呢?喏,看下面

darcy@pemberley.lit

elizabeth@longbourn.lit/room

这就是两个标准的XMPP地址写法,咦,看着好像有点眼熟,这不就是跟邮箱地址差不多嘛,例如123456789@qq.com,对了,不仅长得跟邮箱地址一样,连组成部分都是一样的,@前面的在XMPP协议里叫做节点,可以理解为每个实体的唯一标识,@后面的就做域,也就是XMPP服务器所在的地址,不过跟邮箱地址不同的是,XMPP多了后面的反斜杠/+资源地址,就是上面第二个/room,这个是可选的,也可以不写,下面我们就来详细聊聊JID的三个组成部分:

[ node "@" ] domain [ "/" resource ]

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

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

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

资源:通常会标识一个特定客户端的XMPP连接。对于XMPP客户端而言,每个连接均被指派一个资源。如darcy@perberley.lit想要连接他的房间或图书馆则可以通过darcy@perberley.lit/room和darcy@perberley.lit/library来寻址,这样避免了用户在打开多个链接时消息无法找到正确的处理器。主要注意的是,资源部分是区分大小写的。这样说感觉可能会给各位造成误解,我这样讲吧,资源部分通常用来做多端登录的,就比如我是使用Android手机登录的账号,同时我又用了一台苹果手机登录了同一个账号,与此同时,我又在Window系统的电脑上登录了这个账号,也就是一个账号我同时在三个不同设备上登录了,那么这个时候我可以用资源来标识该用户账号的地址,如darcy@perberley.lit/android,darcy@perberley.lit/ios,darcy@perberley.lit/windows,甚至可以在资源部分加上其它设备参数信息,如ip地址,网络状态等等,这样说,大家应该能够理解了吧,资源就是对节点信息的一个补充而已

JID划分为两种类型:

  • 完整JID,最为具体的地址,也就是上面的三部分(节点@域/资源)组成

  • 裸JID,完整JID去除资源部分的地址(节点@域)

XMPP节

这里的节跟上面的实体地址格式里的节点可不一样哈,是另一个概念,就类似XML里的DOM节点,像这样,和就是两个XMPP节

XMPP发送一条消息或者建立一个连接,一般都是由三个基本节构成,分别为、和,标准写法如下:

<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>

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之外,大家还看到了上边的例子里还有from和to属性,典型的message节中应该还包含from、to或者id属性(用于目的追踪)。

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

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

IQ节

这和我们通常说的IQ智商可不一样,它表示的是Info/Query,为XMPP通信提供请求及响应机制,类似于HTTP协议里的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节

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

总结

有些童鞋看了上边的IQ部分,应该有了发现,XMPP流是由两份XML文档组成的,为什么这么说呢,从客户端发送一个get请求,服务器返回一个result可以看出,XML流是有方向的,因为通信毕竟是双向的嘛,通信的每个方向均有一个文档,这些文档有一个根元素stream:streamstream:stream的子元素由可路由的节以及与流相关的顶级子元素构成。

XMPP连接的生命周期

发送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,简单身份验证与安全层)协议并支持多种身份验证机制(取决于服务器)。

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

当两台服务器相互连接时,身份验证步骤稍稍不同这里只讲客户端与服务器通信,所以服务器和服务器连接暂时不讲

连接断开

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

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

直到目前,我们选择了基于TCP协议的XMPP协议来开发我们要仿写的即时通讯社交App,并且了解了什么是XMPP,它的发展历史以及它的组成部分和规范写法,那么接下来到了本篇文章收尾的部分了

利用XMPP协议开发即时通讯App

XMPP协议只是一套数据通讯规范,并不是我们常用的sdk或library库,如果单就靠XMPP协议,那我们需要去自己做一个后端,并且客户端需要写大量的方法来实现安全,稳定,可靠地与后端交换数据,以此实现通讯,这么做的话,工作量和难度是巨大的,我们程序员的本职工作不就是偷懒嘛,啊呸,是搬砖,正如我的公众号名字一样,我们应该利用好现有的框架,实现高效率开发,刚好使用XMPP协议现成的开源框架还挺多,例如

服务器端

openfire,ejabberd,tigase,Jabberd2,Prosody,还有一些优秀的商业XMPP服务器,比如lsode,Jabber XCP,M-Link

等等,windows,linux,mac系统的都有,在官网(xmpp.org/software/se…

基于XMPP协议的开源服务器端框架库

客户端

也有很多,包含了Anroid,IOS系统,例如smack(本系列文章的主角,这个在官网SoftWare-Client一栏中没有提到,可能是因为官网列出的都是一些完整的开源成品软件项目,非调用库)等等,官网(xmpp.org/software/cl…

XMPP客户端开源库选择

既然我们要做的是Android移动端,那我们就直接选择用Smack,Smack是使用Java编写的开源库,早期Smack团队为Android端单独做了asmack,因为当时虽然Smack在PC上可以工作的很好,功能也很强大,但在Android平台上有一些问题,而导致这些问题的原因是Android精简了Java的类库,以至Smack使用的部分类库在Android平台上无法找到,所以Smack不能直接在Android平台上使用.但在2010年初,有人在code.google.com网站上发布了一个Asmack,其中A库就代表Android中的A,也就是说,这个版本是Smack的Android版本,但是目前Asmack已被标记为废弃且过时,因为从Smack4.1版本开始,无需修改即可在Android上运行,所以我们只需要使用Smack库就够了,如何使用Smack是后面文章需要讲的,本节不会叙述具体使用方法,以及Smack相关的背景和发展历史

对了,除了Smack,笔者最近还了解到还有一个库,Tinder,它也是是基于XMPP的JAVA库,提供XMPP节和组件的实现。但是呢,由于它起源于JIveSoftware的Openfire和Whack实现的共享代码,只是用来代替目前在Openfire、Whack和ConnectionManager共享的一些重复的代码,删除了很多重复部分,所以说Tinder仅仅定义了一些XMPP的构建块,提供了一些基本对象,而Smack提供了一个成熟的XMPP客户端的实现,并且是基于此对象构建的,所以说Tinder不能代替Smack

顺带一提,如果做IOS开发的话,Smack就不适用了,Smack没有IOS版,IOS可以使用XMPPFramework

还有如果要做网页开发的话,想要让JavaScript支持XMPP,可以使用Strophe.js库来实现

XMPP服务器选择

Android端要用的开源库我们选择了Smack,那么服务器端我们该选用什么呢?那我们就来分析下几种常用XMPP服务器的优缺点吧

openfire

ejabberd

tigase

官网

www.igniterealtime.org/projects/op…

(这个官网看起来最low)

www.ejabberd.im/

(大气上档次)

tigase.net/

(大气上档次)

license(开源协议)

Apache

(商业软件可用)

GPLv2

(商业软件不能用)

AGPLv2

(商业软件不能用)

Github Stars

2.2k

github.com/igniterealt…

(不如Ejabberd受欢迎程度高)

4.7k

github.com/processone/…

(star最多)

144

github.com/tigase/tiga…

(star最少)

开发语言

Java(通用,大众)

Erlang/OTP(太小众,门槛高)

Java(通用,大众)

支持并发量

30W

50W

最高并发可达到Ejabberd一样的50W

网络框架

JAVA的MINA

JAVA的NIO

Erlang

优点

成熟稳定,支持插件比较多,基于Java开发,对Android用户有利,支持集群,安装和使用简单

成熟稳定,集群支持强大,拥有Erlang天生的高并发能力

性能更好,支持集群(可以根据需要进行水平横向扩展),并发量也很高

缺点

集群支持相对薄弱,并发数目相对Ejabberd来说较少

语言小众,网上资料比较少且老,部署复杂且坑多,开发成本高

集群方式复杂

如果你是追求性能和高并发的话,那么我会建议你选择Ejabberd和Tigase,但是这两个都不适合用在商业领域,如果非要在这两者之间选出一个的话,那当然是Tigase,基于Java开发嘛,Ejabberd使用Erlang开发,Erlang这门语言太小众了,笔者在做那款应用之前,也有过犹豫,当时心想,多学一门语言也没坏处,学就学嘛,但是在我依次部署了这三种开源XMPP服务器之后,发现Ejabberd,Tigase并不是输在语言方面,而是在在于他们的部署配置和使用方面,我就拿OpenFire和Ejabberd的后台管理界面来做个比较吧,Tigase类似

Ejabberd管理后台界面

openfire管理后台界面

可以看出,Ejabberd的后台功能比openfire要少很多,openfire包含了用户管理,会话管理,聊天室房间管理,参数配置以及开发者上传的插件可供安装使用,后台界面相对来说也更友好,甚至在部署配置方面,也都是全程可视化安装配置,搭建起来方便快捷,而Ejabberd安装起来就没那么容易了,既然这样,我们当然会选择openfire了,既可以商用,安装使用起来还简单,还等什么呢?

到这里,第一节课程的内容基本就讲完了

总结

在常见的网络协议和通信协议中,我们选择了基于TCP协议并且通过XML格式进行数据传输的XMPP协议,又在众多XMPP服务器中选择了Openfire,Android端开发选择了Smack,那么在之后的课程我们将讲解Openfire的诞生背景,发展历程,部署安装和使用方法以及smack结合openfire做出一款可以商用的即时通讯应用

之后的课程我都会放到我做的学习网站上:duoduo.wesee.site,欢迎大家来支持下!

因为本文是从我的公众号:Android搬砖师,转载而来,文章格式稍有不顺还请谅解

小tip:在下节课程开始之前,我建议各位准备一台服务器,域名(可不要,可以通过公有ip直接访问,毕竟备案还是比较麻烦的),服务器建议选择阿里云的,我现在就有两台服务器,一台阿里云的(2核16GB,新用户特惠购买的),还有一台是华为云的(2核4GB)

如果你只是学习使用,不考虑商用的话,前期只要一台学生机就够了,我一开始就是用的学生机,后来用户比较多了,学生机撑不住了,就买了两台配置相对高点的,如果你不是学生的话,学机就没办法买了,但是如果你是阿里云新实名认证用户,可以去淘宝阿里云旗舰店看下,性价比贼高,300左右就能买到相对高性能高配置的服务器,虽然使用时长只有一两年左右,之后就要按原价续费了,原价可不便宜,如果你预算还是不够的话,可以考虑购买轻量云服务器或虚拟主机,性价比也比较高,学习使用的话足够了