近日工作闲暇之余,对IM系统产生了兴趣,转而研究了IM的内容。找了半天,知道比较流行的是Openfire的系统,Openfire有许多平台实现,由于我是做Web的,所以当然是希望寻找Web的实现。Openfire的以前的Web实现,是基于Http-bind的一种长轮询机制,当然也没什么不好,只是我现在HTML5都开始了,当然希望能够来个基于Websocket的机制了。然而Google&百度了好久,也没有找到什么教程,发现这个东西并不是很火热的样子,那只好自己开始研究了。于是有了这一篇文章。

  对了,我知道xmpp的JS框架有几个,但那些框架似乎都是按照原本的长轮询的机制来做的,并不是使用Websocket来做的。

  另外,做软件2年了,当我刚涉足这个行业时,我被告知:“不要重复造轮子!”,曾经我对此深信不疑,但这两年的工作让我越发认识,不要重复造轮子,仅仅是在于做项目当中,做一个商业产品的时候,考虑到开发速度和产品后期的稳定性及维护性,确实需要采用成熟的技术,但!这不代表作为一个程序员,不应该抱着一种从0开始的研究精神,如果真的热爱这个行业,这个领域,就应该尝试着,根据RCF文档协议,从基础协议的层次开始做软件。

    好了,废话不多说。恩,开始正文吧。

So 得先搞一个服务端是吧?


  肯定是去 Openfire 的官网下载最新的Openfire服务端程序啦,Openfire的是开源的,可以免费下载。如图:

  1. Openfire 的主程序,主程序是服务端程序,这个主程序不是一个框架,不是一个半成品,而是一个很完整的项目,怎么个很完整?就是你下载下来,直接双击运行(Windows),或到其程序的根目录下bin文件夹,然后执行 openfire start 命令(Linux),Mac就更简单了,直接在“设置”当中会有一个专门的管理窗口。然后它有一个Web版的管理后台,所有设置都可以在这个后台完成,包括安装插件之类的。
  2. 这个火花(Spark)是Openfire搭配的客户端,也是多平台支持的。
  3. 这个地方可以下载Openfire的源代码,你可以把他放到你的IDE中,然后简单配置一下,就可以run起来,这个教程网上很多,我就不多废话了。

  

安装WebSocket插件


  

  这就不说了。

建立Websocket连接


  安装了WebSocket插件之后,有两个原本用于http-bind的端口,就被WebSocket占用了(有了WebSocket,还需要Http-bind做啥= =)。这两个接口分别是7443和7070,前者是用于HTTPS安全连接,后者是非安全连接。

  建立链接:

   var connectionState = ["正在连接..", "连接已建立", "正在关闭..", "已经关闭"];
var host = "ws://127.0.0.1:7070/ws/";
if (window.WebSocket != 'undefined') {
//OpenFire是实现了WebSocket的子协议
var connection = new WebSocket(host, "xmpp");
console.log(connectionState[connection.readyState]);
//注册连接建立时的方法
connection.onopen = wsOpen;
//注册连接关闭时的方法
connection.onclose = wsClose;
//注册收到消息时的方法
connection.onmessage = wsMsg;
}

  如果要使用Https加密信道,就把Host改成:

var host="wss://127.0.0.1:7443/ws/

  恩,建立安全连接还需要添加安全证书到Keystore,这个在Openfire的根目录下,有一个resource/security文件夹,里面有keyStore文件,当然,这部分我还不是很懂,关于Https的加密信道,TSL/SSl证书的概念,还没完全弄明白,不过这也不是这篇文章的重点。暂时我就先用非加密的方式来做,至于加密的连接,除了host的区别,其他也没有区别。

  当Websocket握手之后,我们要做的第一件事,就是发起一个建立流的请求,

 function wsOpen(event) {
//打印链接状态
console.log(connectionState[connection.readyState]);
//发送建立流请求
var steam = "<open to='127.0.0.1' from='wuxinzhe@127.0.0.1' xmlns='urn:ietf:params:xml:ns:xmpp-framing' xml:lang='zh' version='1.0'/>";
connection.send(steam);
}

  Websocket下,建立流不像其他平台那样,使用<stream:stream/>标签,而是使用<open/>标签,其中to属性是域名,from是你的JID。

  发出请求之后,会立刻收到服务器的响应:

  第一条响应式服务器同意建立流,第二条是告诉你,安全验证的几种方式,其中最简单的方式是PLAIN方式,这种方式仅仅是将你的账号密码进行BASE64加密后传输,可以说是很不安全。当然,你也可以选择SCRAM-SHA-1的安全加密方式,只不过你的js库要支持SHA-1加密,我因为是刚开始探路,所以一切从简,SHA-1的加密方式请求流程跟PLAIN会有一点区别,回头咱们再说。

  另外我这边写了一个当收到来自服务端信息的方法:

  function wsMsg(event) {
console.log("Server: " + event.data);
}

  打印出来而已。就像上面console面板中的信息一样。那个是chrome的调试器,其他浏览器也有对应的。

发起登录验证


  刚才说了,我先用最基本的PLAIN的方式登录:

   function auth() {
//Base64编码
var token = window.btoa("wuxinzhe@127.0.0.1\0wuxinzhe");
var message = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" + token + "</auth>";
console.log("Client: " + message);
connection.send(message);
}

  其中,window.btoa()是自带的方法,不需要额外加载任何库,所以我才说这个真的是最快最简单的方法,然后我们看方法体内的字符串,这个字符串格式是:jid+password,以\0作为分隔符,后端java程序是一个DefaultAuthProvider提供验证的,当然你们也可以实现自己的验证方式,默认的方式是以\0作为分隔符,注意的是,我一开始密码是123456,发现\0123456这样到后端密码会被分割成23456,\01整个会被转译,恩,解决的办法要嘛就换一个分隔符(我是说后端的openfire那边),要嘛就禁止以数字开头的密码。

  发起安全验证之后,会受到服务端的响应, 如果成功了,如图:  

  至于要是失败了,会返回这个错误,当然这个错误信息很少,到底是用户名找不到,还是密码错误了,单凭这个错误信息是看不出来的,不过也没办法咯,要不,去修改一下服务端呗:

  当我们发起安全验证成功了以后,紧接着就要开启一个新的流,新的流服务端会给予一些新的XML节点权限(<iq/>、<presence/>),这样才能发送一些其他功能的信息,比如发送消息,获取联系人列表,再刚开始建立的第一个流失不能发送这些节点的。

  建立新流同样适用<open/>标签,但有一个地方与之前不同,就是这次是需要携带id属性的,什么是id属性?我们回顾第一个流建立时,服务端返回的<open/>信息,是不是就有一个id,没错,这个id据我的理解,每次建立websocket连接时,都会为每个连接生成一个独一无二的id,这个id代表了这个连接,所以后续我们会在很多很多地方都需要使用这个id。

  发起新的流:

 <open xmlns='jabber:client' to='127.0.0.1' version='1.0' from='wuxinzhe@127.0.0.1' id='70tvu3ooiu' xml:lang='zh'/>

  服务端会返回两条信息,第一条,是同意打开新流,第二条,是告诉你,接下来要做的是bind操作,就是要绑定客户端:

  发起绑定也要用到刚才说的id属性:

<iq type='set' id='6ps7q3ideb'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>

  绑定的时候,还可以加入一些标签,来对当前的客户端,做一些比较具有语义的说明,用来描述你的客户端类型:

<iq id="wSBRk-4" type="set">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>Showings</resource>
</bind>
</iq>

  我们看看这两种bind之间的区别:

  这是bind的客户端请求内容和服务端响应内容,上面的是没有加入<resource/>标签的,下面是加入了以后。我们可以看到,服务端返回信息中的<jid/>是有区别的,当不加描述节点的时候,是将前面的id属性直接用作后缀拼接如JID的,加入了以后自然会更具语义化。

  这个过程似乎是Openfire用于区分登录的客户端类型的方式。这样如果同一个账号,在不同的客户端登录,也会有所区别。  

  然后我们要获取session。

<iq xmlns="jabber:client" id="ak014gz6x7" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>

  这部分我暂时还不知道用来干嘛的,毕竟我还没熟悉openfire的xmpp协议的整个过程,所以有些部分不是很清楚,待我整个看过以后,到时候再来看看这个步骤是做什么用的。

  此时我们进入Openfire的后台,看看用户在线的情况:

  诶?怎么有连接,却是离线呢?不着急,因为我们虽然登陆了,但是我们还没有“出席”。就像QQ你可以设置不同的登录状态,有在线、不在电脑、忙、离线,这些状态,所以我们如果要在线,只需要发送出席请求就行了:

<presence id="ak014gz6x7"><status>Online</status><priority>1</priority></presence>

  你们看,到处都要用到这个ID,当然,前面我们做了绑定动作,此刻不用id属性,换成from="jid"应该也是有效的。

  此时我们再看后台状态:

  OK了。

  So我们还要下线呢,关闭连接,此时要用<open/>对应的标签<close/>

<close xmlns="urn:ietf:params:xml:ns:xmpp-framing"/>

  这样就行了。

   其实使用Websocket建立连接与XMPP协议在其他的客户端里是没有什么太大的区别,可能就是<open/><close/>这两个标签的区别。我们现在能够顺利登录了,基本上,就是有一个好的开始了。

[Openfire]使用WebSocket建立Openfire的客户端的更多相关文章

  1. Smack 结合 Openfire服务器,建立IM通信,发送聊天消息

    在文章开始,请你了解和熟悉openfire方面的相关知识,这样对你理解下面代码以及下面代码的用途有很好的了解.同时,你可能需要安装一个简单的CS聊天工具,来测试你的代码是否成功的在openfire服务 ...

  2. (转)基于即时通信和LBS技术的位置感知服务(三):搭建Openfire服务器+测试2款IM客户端

    主要包含4个章节: 1. Java 领域的即时通信的解决方案 2. 搭建 Openfire 服务器 3. 使用客户端测试我们搭建的 Openfire 服务器 4. Smack 和 ASmack 一.J ...

  3. 基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件[转]

    上一篇文章介绍到怎么在自己的Java环境中搭建openfire插件开发的环境,同时介绍到怎样一步步简单的开发openfire插件.一步步很详细的介绍到简单插件开发,带Servlet的插件的开发.带JS ...

  4. openfire:基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件

    基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件 上一篇文章介绍到怎么在自己的Java环境中搭建openfire插件开发的环境,同时介绍到怎样一步步简单的开发openfir ...

  5. 基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件

    原文:http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html 随笔-150  评论- ...

  6. 在Ubuntu6.06 在搭建SVN服务器及在windows建立svn+ssh客户端 (续)

    接上篇.本篇主要介绍windows下建立svn+ssh客户端. 9.在windows下安装svn客户端,则需安装“TortoiseSVN”.“Puttygen”和“Pageant”    9.1.下载 ...

  7. Openfire分析之一:Openfire与XMPP协议

     引言 上帝说,要有光,于是就有了光. 有点玄. 如果将时光回溯无数岁月,到几百万年的蛮荒时代,人类史上第一次发生信息交换,会是什么样子?是转一下脑袋,还是眨一下眼? 但不管是什么形式,于是有了信息, ...

  8. 即时通信系统Openfire分析之一:Openfire与XMPP协议

     引言 目前互联网产品使用的即时通信协议有这几种:即时信息和空间协议(IMPP).空间和即时信息协议(PRIM).针对即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)以及XMPP.PRIM与 ...

  9. xmpp openfire smack 介绍和openfire安装及使用

    前言 Java领域的即时通信的解决方案可以考虑openfire+spark+smack.当然也有其他的选择. Openfire是基于Jabber协议(XMPP)实现的即时通信服务器端版本,目前建议使用 ...

随机推荐

  1. ArcGIS制图表达Representation-符号制作

    ArcGIS制图表达Representation-符号制作 by 李远祥 在ArcGIS的符号里面,存在着两种符号体系,一种是传统的标准符号体系,一种是制图表达符号体系.标准符号几乎被绝大部分ArcG ...

  2. vue.js 常用语法总结(一)

    作者:曾萍,目前就职于京东商城. 概述 2016年已经结束了.你是否会思考一下,自己在过去的一年里是否错过一些重要的东西?不用担心,我们正在回顾那些流行的趋势.通过比较过去12个月里Github所增加 ...

  3. 浅谈时间复杂度- 算法衡量标准Big O

    写在前面: 今天有一场考试,考到了Big-O的知识点,考到了一道原题,原题的答案我记住了,但实际题目有一些改动导致答案有所改动,为此作者决定重新整理一下复杂度相关知识点 Efficiency and ...

  4. PHP 中使用 Composer

    在线安装版本: http://www.phpcomposer.com/ 这个是国内的composer网站 thinkphp5自带了composer.phar组件,如果没有安装,则需要进行安装 以下命令 ...

  5. 浅谈V8引擎中的垃圾回收机制

    最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...

  6. css3动画animate

    CSS3 动画 通过 CSS3,我们能够创建动画,这可以在许多网页中取代动画图片.Flash 动画以及 JavaScript. @keyframes 定义动画关键帧: @keyframes anima ...

  7. BZOJ 1834: [ZJOI2010]network 网络扩容(网络流+费用流)

    一看就知道是模板题= = ,不说什么了= = PS:回去搞期末了,暑假再来刷题了 CODE: #include<cstdio> #include<iostream> #incl ...

  8. android学习16——library project的使用

    library project和普通的project没有区别.用如下命令新建的一个工程. android create project --target 3 --name MyActivity --p ...

  9. 编程思想之——"人是活的,程序是死的"

    "人是活的,程序是死的"这句话我时常提起,可能很多人不是很理解我为什么会这样说,下面我就简单来谈谈我对这句话的理解. 1.不要因为技术而技术,技术选型的初衷是需求. 现在很多人在做 ...

  10. C#开发step步骤条控件

    现在很多的javascript控件,非常的不错,其中step就是一个,如下图所示: 那么如何用C#来实现一个step控件呢? 先定义一个StepEntity类来存储步骤条节点的信息: public c ...