跨平台XMPP企业即时通信系统开发基础

来源:互联网 发布:网络安全法出台 编辑:程序博客网 时间:2024/04/29 18:48

Openfire+Spark开发建议:

1. Openfire服务器端

(1)支持的数据库类型和插件太多,为简化工作量,是否考虑支持支持MySQL/Oracle/MS-SQL几种常用的数据库;

(2)只是支持简体中文和英语两种语言

(3)只支持和完善我们需要的几种插件,去掉其余的功能

(4)服务器端安装成功之后,试用用户名(admin)和密码(admin)管理平台时,可能会提示用户名和密码不正确。这时,只需重启服务器端即可征程登录控制平台。

2. Spark客户端

(1)同一台电脑只能同时启动1个客户端,需要修改为同时可以启动多个客户端;

(2)修改Openfire和Spark的界面及其部分功能,以很好地满足我们当前的需求。

3. 开发过程中的约定

(1)使用SWT+JFace重新开发客户端的界面,现有的Spark的界面是使用Sun公司的AWT开发的;

(2)对于重要的类和接口,编写明确说明其功能的Javadoc,以便后续开发和更新版本;

4. 基于Smack API开发BXEIM智能客户端

(1)创建一个chat

(2)使用log(日志)

(3)监听数据库记录变化:创建1个线程,每隔一段时间访问一次数据库。

(4)采用数据库并发的方式实现,智能XMPP IM客户端的消息并发功能。

(5)Smack API自身就可以实现消息并发的功能。

5. 配置Openfire+Spark支持视频的方法

参考网址:(1)http://www.igniterealtime.org/community/docs/DOC-1633

(2)http://www.blogjava.net/webjlwang/archive/2009/04/21/112337.html

环境要求:需要jre1.6的环境,原因是red5是在jdk1.6下编译的

(1)到http://www.igniterealtime.org/projects/openfire/plugins-beta.jsp 在这里下载red5.war这个插件,然后放到openfire目录下的plugin下,jetty会自动解压。

(2)安装后,将red5包中的red5-plugin.jar复制到spark的plugin目录中

(3)然后,就可以用spark进行视频通讯了。

所需插件下载地址:

http://www.igniterealtime.org/community/servlet/JiveServlet/download/1633-5-3227/openfire-red5-spark-rtmps-required-files.zip

 

 

第1节  基于Smack API与Openfire的应用开发基础

 

1. Smack API应用开发基础

Smack 3.0.4类库压缩包解压之后主要有smack.jar、smackx.jar、smackx-debug.jar和smackx-jingle.jar共4个类库文件,以及类库的Javadoc文档与开始说明文档(包括XMPP核心协议的开发说明与XMPP扩展协议,如文件传输协议的开发说明)。其中,基于Smack的开发只需要将smack.jar、smackx.jar类库文件添加到Java项目的构建路径中即可,smackx.jar和smackx-jingle.jar实现了XMPP的扩展协议,smackx-jingle.jar重点实现了基于P2P方式的文件、视音频等多媒体数据的传输协议Jingle;smackx-debug.jar是Smack内置的一个增强型的调试器,能够使开发者跟踪客户端与服务器之间的所有XML传输。Smackincludes two built-in debugging consoles that will let you track all XMLtraffic between the client and server.

1.1 Debugging with Smack(使用Smack内置的调试器)

(1)将Smack内置的增强型调试器smackx-debug.jar添加到项目的构建路径中;

(2)在创建一个连接之前(before creating new connections),增加如下一条代码就可以启动Smack内置的调试器,从而对代码进行调试;

                  XMPPConnection.DEBUG_ENABLED= true;

(3)程序编写完成,发布程序时只需将true修改为false即可关闭Smack内置的调试器。

使用Smack内置的增强型调试器对代码进行调试,可以很容易地发现代码中的问题出现在哪里。Smack内置的增强型调试器如下图所示。

图23.4.1  EnhancedDebugger(增强的调试器)

1.2 Smack内置的增强型调试器

(1)连接标签(Connection tabs )显示显示与连接有关的调试信息

  所有的信息包(All Packets):显示由Smack解析的发送和收到的信息包的信息。

  未经处理的发送信息包(Raw Sent Packets):未经处理的XML traffic(raw XML traffic)由Smack生成并发送至服务器。

  未经处理的接收信息包(Raw Received Packets):未经处理的XML traffic(raw XML traffic)由服务器发送给客户机。

  Ad-hoc 消息(Ad-hoc message):允许发送各种类型的ad-hoc信息包(ad-hoc packets)。

  信息:显示连接状态和统计信息。

(2)Smack配置信息标签(Smack info tab)

显示Smack的版本、已安装的组件等配置信息。

1.3 简单调试器(Lite Debugger)

当调试模式可用时,每创建一个连接将出现调试窗口,该窗口包含以下信息:

(1)客户端的流量(Client Traffic) (红色的文本)

未经处理的XML traffic(raw XML traffic)由Smack生成并发送至服务器。

(2)服务器端的流量(Server Traffic)(蓝色的文本)

未经处理的XML traffic(raw XML traffic)由服务器发送给客户机。

(3)解释的信息包(Interpreted Packets)(绿色的文本)

显示来自服务器的由Smack解析的XML信息包(XML packets)

1.4 Openfire服务器的配置

这里主要阐述在使用Smack API开发用户自己的客户端时,Openfire服务器端需要进行的一些设置,否则无法得到正确代码测试反馈信息。

(1)使用MySQL数据库做服务器端的数据库时,不能正常显示用户的中文昵称,但使用Oracle数据库就可以正常显示用户的中文昵称等。

(2)Openfire连接Oracle 10g时,如果服务器上安装的是JDK 1.6,使用Oracle 10g对应的JDBC连接Oracle 10g数据库,Openfire服务器就会提示JDBC驱动错误。这时因为Oracle 10g对应的JDBC使用的JDK是1.4和1.5的版本,不适合JDK 1.6环境,所以需要使用Oracle 11g中用于JDK 1.6的JDBC(如JDBC 11.1.0.6驱动程序)就可以顺利完成Oracle 10g数据库的连接与配置。

(3)如果数据库中已经存在相应的数据表,重新安装Openfire服务器之后,使用同一个用户连接这个数据库时,若出现错误,将原数据库中与Openfire相关的数据表删除之后即可成功连接数据库。

 

2. Smack API 3.0.4分析

2.1 创建XMPP连接并登录服务器

(1)配置XMPP连接

ConnectionConfiguration config

= newConnectionConfiguration(host, port, servicename);

这里的服务器主机(host)可以是服务器的IP地址,也可以是服务器的域名,如baixin.com;客户端采用新版本的TLS (SSL)连接openfire服务器时,默认的端口号是5222;服务名(servicename)可以是服务器的计算机名。

最新版本的TLS(Transport Layer Security,传输层安全协议)是IETF(Internet Engineering Task Force,Internet工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本。设置用户是否采用新的SSL安全连接服务器的方法是ConnectionConfiguration .setSecurityMode()方法。

(2)创建XMPP连接并登录XMPP服务器

XMPPConnection connect= new XMPPConnection(config);

connect.login(username,password,resource, isSendPresence);

这里的用户名(username是XMPP ID中@前面的部分,如liaolonglong@baixin.com中的liaolonglong;密码(password是用户登录XMPP服务器的密码;资源(resource用户可以任意指定,可以指定为Smack等;若要获取该用户的离线消息,则参数isSendPresence须为false,否则无法获取用户的离线消息。

(3)离线消息的获取与删除

首先,需要创建XMPPConnection对象之后,用户登录服务器,利用创建的XMPPConnection创建并初始化offlineMessageManager对象offlinemsgmanager;

其次,使用XMPPConnection类的login("username","password","resource",false)方法登录服务器,而且这个方法的第四个参数的值必须是false,否则无法获取用户的离线消息;

然后,使用offlinemsgmanager对象的相关方法,获取、保存、处理用户的离线消息。

接着,成功获取并保存离线消息之后,需要使用offlineMessageManager类的deleteMessage()方法删除本次的离线消息,否则再次登录时仍会获取本次的离线消息。

最后,发送用户的在线消息包。

Presence presence = new Presence(aType, description, 0, aMode);

其中,aType表示Type,description表示登陆描述,可随便写,aMode表示mode

例如:

                  ConnectionConfigurationloginconfigure =new ConnectionConfiguration("192.168.10.99", 5222,"Smack");        

                  XMPPConnectionxmppconnect =newXMPPConnection(loginconfigure);      

                  try{

                          System.out.println("正在连接服务器......");

                          xmppconnect.connect();

                          //*****************************

                          OfflineMessageManager  offlineManager =newOfflineMessageManager(xmppconnect);                         //*****************************

                          System.out.println("正在登录服务器......");

                          xmppconnect.login("mou","123456","Smack",false);

                          System.out.println("正在获取离线消息......");

                          System.out.println("您有"+offlineManager.getMessageCount()+"条离线消息");

                          System.out.println("您已登录成功,可以开始聊天");                    

                  }catch(Exception et){

                          System.out.println("异常处理");

          }

 

 

2.2 用户名册(Roster)及其条目(Entries)

2.2.1花名册(Roster)

花名册(Roster)让你很清楚地知道其他可用的用户,而且用户可被分成像“朋友”、“合作者”这样的组,从而知道其他用户的状态,如在线还是离线。可以使用XMPPConnection.getRoster()方法检索花名册;可以用花名册(roster)类查找花名册的所有条目,以及它们所属的组以及每个条目当前呈现的状态。

2.2.2花名册中的条目(Entries)

花名册里的每一个用户都以一条花名册条目的形式呈现,包括以下几部分:

(1)一个XMPP地址(例如:jsmith@example.com).

(2)分配给你的用户名 (例如:"Joe").

(3)该条目在花名册中所属组的列表。如果该条目不属于任何一个组,将被称为“尚未分类的条目”。

以下程序段可以打印出花名册中的所有条目:

Roster roster = con.getRoster();

for (Iterator i=roster.getEntries(); i.hasNext();)

{System.out.println(i.next());}

也有获得个人条目、尚未分类条目的列表、一个或者所有组的方法。

登录时判断用户是否已经登录,需要将该用户添加到自己的名册中,这样应该可以很方便地获取用户的出席信息。

2.2.3 出席(Presence)

花名册中的每一个条目都有相关的出席方式。Roster.getPresence(String user)方法将通过用户的状态或当用户不在线或不同意将其在线状态显示出来时使用空对象(null)返回一个Presence对象。

2.2.4 向花名册中添加条目(Adding Entries to theRoster)

花名册和显示使用基于许可的模型,这要求用户在加入别人的花名册前必须得到允许。这样,确保只有被允许的人才可以看到自己所显示的信息,从而保护了用户的隐私。因此,在你想添加一个新的条目,且对方没有接受你的请求前,该条目将处于等待状态。

如果另一个用户请求同意显示,从而你他们可以将你加入他们的花名册,你必须接受或拒绝请求。Smack通过以下三种方式之一操作同意显示请求:

自动接受所有的同意显示请求。

自动拒绝所有的同意显示请求。

手动处理同意显示请求。

可以使用Roster.setSubscriptionMode(int subscriptionMode)方法设置模式。简单的客户通常使用一个自动接受或拒绝同意显示请求的模式,而用更多特征的用户应该使用手动处理同意显示请求的模式,并让终端用户接受或拒绝每一个请求。如果使用手动模式,应该声明一个信息包监听器(PacketListener)来监听有Presence.Type.SUBSCRIBE类型的显示信息包。

2.2.5 处理收到的信息包(Processing IncomingPackets)

Smack提供一个使用以下两个结构的灵活框架来处理收到的信息包:

   (1)org.jivesoftware.smack.PacketCollector是一个允许你同步的等待新的信息包的类

   (2)org.jivesoftware.smack.PacketListener是一个异步的通知你收到信息包的接口

信息包监听器(packet listener)用于事件类型的设计,而信息包收集器(packet collector)有一个信息包的结果队列,你可以对其实施polling和blocking操作。所以,信息包监听器在你收到任何一个信息包,且你想对其进行操作时是有用的,而信息包收集器在你想等待某个特殊的信息包时是有用的。信息包收集器和监听器可以通过XMPPConnection的实例来创建。

由org.jivesoftware.smack.filter.PacketFilter接口来决定哪个特殊的信息包将被转交给信息包收集器(PacketCollector)或信息包监听器(PacketListener)。可以在org.jivesoftware.smack.filter包中找到许多预先定义的过滤器。

通过给用户创建的XMPP连接添加消息监听器并处理收到的消息包,可以实现离线聊天信息的接收、获取用户的状态及其状态的改变情况。

例程:

/**

          *通过包监听器接收他人发给用户的消息

          *@paramconnect用户登录服务器时创建的XMPP连接

          */

         publicvoid chatMessegeListener(XMPPConnection connect){

         //用户已创建的一个XMPPConnection

         XMPPConnection con = connect;

/*      // Create a packet filter to listen for new messages from aparticular

         // user. We use an AndFilter to combine two other filters.

          PacketFilter filter =new AndFilter(new PacketTypeFilter(Message.class),

          newPacketTypeFilter(Presence.class) );*/

         //创建一个监听消息接收与发送的过滤器messagefilter

         PacketFilter messagefilter =newAndFilter(new PacketTypeFilter(

                          Message.class));

         //(1)使用已创建的过滤器messagefilter注册一个信息包收集器(packet collector

         //register a packet collectorusing the filter we created.

         PacketCollector myCollector =con.createPacketCollector(messagefilter);

         //2)通常,可以使用已创建的信息包收集器(packet collector)进行一些处理,如等待新的数据包的到来

 

         //(3)使用匿名内部类(anonymous inner class)来创建包监听器(packet listener

         PacketListener myListener =newPacketListener() {

                          publicvoid processPacket(Packet packet) {

                                   // 对收到的数据包进行处理                   

                                   String pcomefrom =packet.getFrom();

                                   System.out.println("信息来自:" + pcomefrom);

                                   //Returns the unique ID of the packet.

                                   String pthreadid =packet.getPacketID();

                                   System.out.println("ID:" + pthreadid);

                                   String pto = packet.getTo();

                                   System.out.println("发给:" + pto);

                                   //处理消息包

                                   if (packetinstanceof Message) {

                                           Message mesg = (Message)packet;//类型转换

                                           String mesgcontent =mesg.getBody();

                                           System.out.println("消息的内容是:" +mesgcontent);

                                   }

                          }

                  };

   //(3)注册(Register)消息监听器与包过滤器

                  con.addPacketListener(myListener, messagefilter);

         }

2.2.6 标准信息包过滤器(Standard Packet Filters)

Smack包含一套丰富的信息包过滤器,你也可以通过信息包过滤器接口(PacketFilter interface)编写程序来创建自己的过滤器。缺省的过滤器集包括:

(1)PacketTypeFilter:某个特殊的类类型的信息包过滤器

(2)PacketIDFilter:拥有特殊的信息包ID(packet ID)的过滤器

(3)ThreadFilter:拥有特殊线程ID(thread ID)的信息包的过滤器

(4)ToContainsFilter:发送到某个特殊地址的信息包的过滤器

(5)FromContainsFilter:发送到某个特殊地址的信息包的过滤器 

(6)PacketExtensionFilter:拥有特殊的信息包扩展的信息包的过滤器

(7)AndFilter:对两个过滤器实施逻辑与操作的过滤器

(8)OrFilter:对两个过滤器实施逻辑或操作的过滤器

(9)NotFilter:对一个过滤器实施逻辑非操作的过滤器

2.2.7 信息包属性(Packet Properties)

Smack提供简单的机制来将任意的属性附加到信息包上,每一个属性有个字符串类型的名字和一个值,这个值或者是Java原始数据类型(int, long, float, double, boolean)的,或者是任何可序列化的对象(Serializable object)(当一个java对象实现了Serializable接口时,它就是可序列化的)。

 

3. Openfire的用户管理

(1)User Service插件的使用

这里阐述把openfire与已有系统的用户数据整合起来,实现openfire用户同步添加、删除、修改的方法,因为使用md5加密出来的数据与openfire的加密数据是不一样的,需要使用官方网站上公布的用来做用户管理的UserService插件。

User Service插件的作用就是允许程序设计师通过http管理openfire的用户。The User Service Plugin providesthe ability to add,edit,delete users by sending an http request to the server.It is intended to be used by applications automating the user administrationprocess.

User Service插件在Openfire服务器端部署以后,默认情况下user service是没有开启的,需要登录Openfire服务器后台,将其开启并且设置验证码;为了确保安全,还要设置一个安全的IP地址。这样,完成Openfire服务器部署之后,才可以使用User Service插件的说明文档中的方法及其参数,采用http方式同步管理Openfire的用户数据。When sending double characters(Chinese/Japanese/Korean etc) you should URLEncode the string as utf8.In Javathis is done like this URLEncoder.encode(username, "UTF-8"))。

(2)使用户自己也显示在自己所在的花名册(Roster)中

在新建用户组时,如果选择了“开启联系列表组共享”及其“所有用户”或“下列组”,就需要在如下图所示的红色区域的文本框中输入当前用户组的名称,否则,客户端无法正确读取当前用户组的名称及其个数。同时,可以通过添加用户JID的方法可以向该组中加入新的组员。

完成以上创建组、添加组成员之后,默认情况下,用户本身并不会出现在自己的花名册(Roster)中。在如下图所示的页面中,点击“Add New Item”,使用用户自身的JID,将用户自身添加到用户自身的花名册(Roster)中。但这时,用户所在的组显示为“none”。

在上图所示的页面中,单击用户自身所在项目中的编辑(Edit),弹出如下图所示的对话框,输入用户的昵称、所在的组名,并将Subscription项的值设置为Both,单击“保存”按钮,重启Openfire服务器。这时,用户将会出现在自己所在的花名册(Roster)中。

4. log(日志)

官方主页:http://logging.apache.org/log4j/

Log4j中有三个主要的组件,它们分别是 Logger、Appender和Layout,Log4j 允许开发人员定义多个Logger,每个Logger拥有自己的名字,Logger之间通过名字来表明隶属关系。有一个Logger称为Root,它永远存在,且不能通过名字检索或引用,可以通过Logger.getRootLogger()方法获得,其它Logger通过 Logger.getLogger(String name)方法。

Appender则是用来指明将所有的log信息存放到什么地方,Log4j中支持多种appender,如 console、files、GUI components、NT Event Loggers等,一个Logger可以拥有多个Appender,也就是你既可以将Log信息输出到屏幕,同时存储到一个文件中。

Layout的作用是控制Log信息的输出方式,也就是格式化输出的信息。Log4j中将要输出的Log信息定义了5种级别,依次为DEBUG、INFO、WARN、ERROR和FATAL,当输出时,只有级别高过配置中规定的级别的信息才能真正的输出,这样就很方便的来配置不同情况下要输出的内容,而不需要更改代码,这点实在是方便啊。

 

第2节  Openfire源代码分析研究

 

1. 直接编译、调试、打包Openfire源代码

编译调试的环境(IDE):Eclipse 3.3.2+JDK1.6+Ant(内置)+openfire_src_2008-06-04.zip

在Windows操作系统下编译、调试openfire之前,需要确保HTTP SSL服务已经启动。否则,会出现各种错误信息。

1.1 获取Openfire源代码

从官方网站上直接下载官方发布版本的源代码openfire_src_2008-06-04.zip源代码压缩包;

1.2 创建Openfire工程

将openfire_src_2008-06-04.zip解压后,目录下除了READEME.html、LICENSE.html和changelog.html三个网页文件之外,有下面四个子目录。

build目录:build目录下收录的是生成安装文件(例如:rpm)所要的一些文件,例如JRE等。

resources目录:resources目录下收录的是一些为实现国际化(i18n)和本地化的一些编码文件(例如:英文,中文,法文,德文等)。

documentation目录:documentation目录下收录的是一些关于Openfire安装和配置的信息,但最终要的是这里有Openfire开发的Javadoc。

src目录:这个src文件夹就是我们想要的Openfire源代码了,这下面又有许多文件夹,我们只要Java文件夹就好,这里面实现的Openfire的核心功能,通过它就可以调试Openfire。

(1)从现有资源新建项目:File->New->Java Project,在弹出的NewJava Project对话框中输入工程名:openfire,选择Create project from existing source项,单击Browe,找到当前工作空间目录(\workspace目录)下存放源代码的openfire文件夹。单击Next->Finish按钮。

注意:新建工程之后,可能会出现一些警告信息,下面对此予以说明:

对于警告信息,是没有关系的。因为这些警告信息是使用1.5以上版本JDK时候才出现的,使用JDK1.4是不会出现这些警告信息的。因为这些代码中的有些是使用JDK 1.4编写的,而JDK1.4不支持泛型,而JDK 1.5和JDK1.6是支持泛型的。这些警告信息主要是提示将这些非泛型的代码修改为更加高级的方法——泛型,所以这些警告信息是不影响代码结果的。如果需要修改为泛型也是可以,使用泛型来处理相关的集合类。

(2)在Eclipse中将此Java文件夹当成源代码文件夹新建工程,建立新工程后观察Openfire所import的包,发现Openfire用了许多开源工具,而这些库都在上文提到的Build目录的lib目录下(当然也可以到网上下载),将Openfire用到的库都添加到这个新建工程的Referenced Libraries中。或者如果这样一个一个添加比较麻烦的话还有一个方法就是从你安装好的Openfire服务器目录下的lib目录中(例如:“D:\ProgramFiles\Openfire\lib”或“/usr/local/openfire/lib”)找到openfire.jar,打开后将里面的org/jivesoftware删除,重新打包成jar并添加到这个新建工程的ReferencedLibraries中,这样省去了很多工作。

(3)openfire的起始类为org.jivesoftware.openfire.starter.ServerStarter.java 但是直接运行此类却有问题,因为此类是针对Openfire安装包而设计的,此类的功能是将所用到的Jar文件解压并将class文件加载到虚拟机中,而我们要用的却是源代码中我们自己编译好的class文件,所以我们需要一个新的启动类,一个简单的实现如下(当然最好是与ServerStarter.java中的方法一样,用自定义的ClassLoader来将XMPPServer.class加载到虚拟机中):

 

 packageorg.jivesoftware.openfire.starter;

 

 importorg.jivesoftware.openfire.XMPPServer;

 

public class StandaloneStarter {

 

     public static voidmain(String[] args) {

      XMPPServer server = newXMPPServer();

    }}

还有一个关键的一步是Openfire的Home没有设定,即在XMPPServer类中有一个locateOpenfire方法,这个方法就是设置openfireHome属性。

具体修改如下:

// and just look for home in a standard sub-dir location and verify

// by looking for the config file

if (openfireHome == null) {

try {

//修改的是下面的代码,将".."替换为其他路径了

openfireHome = verifyHome("C:\\Program Files\\Openfire",jiveConfigName).getCanonicalFile();

}

catch (FileNotFoundException fe) {

// Ignore.

}

catch (IOException ie) {

// Ignore.

}

}

这部分默认是找当前路径,你可以修改它为你安装Openfire的路径,这样问题就解决了。

1.3 编译工程

openfire是用ant构建的,所以要先设置使得eclipse中每次编译都使用内置的ant工具,保证编译的正确执行。同时,要在Eclipse中设置为使用JDK作为JVM,如果使用JRE作为JVM,编译就会失败。

因为openfire在Windows下是使用install4j打包成.exe格式的安装文件的,所以需要安装install4j。否则,在使用ant进行编译的时候,在ant视图中可能会出现警告如下信息:taskdef classcom.install4j.install4jtask cannot found。这主要是要安装用来打包Java应用程序的install4j.exe程序,而且将要安装到其默认的安装路径C:\ProgramFiles\install4j下。

注意:有时候在Windows下安装的时候,可能会出现如下图所示的错误,而导致无法安装

但是,只要将安装程序install4j_windows_4_1_2_with_jre.exe拷贝到上面提示的目录下进行安装就可以安装成功,如复制到C:\Documentsand Settings\liaolonglong目录下。

(1)在eclipse中点击Window->Show View->Ant,打开Ant窗口;

(2)在Ant视图中,单击右键,在弹出的快捷菜单中选择Add Buildfiles..;

(3)在弹出的对话框中,展开openfire/build文件夹,选择build目录下的build.xml,点击OK;

(4)在Ant视图中, 展开OpenfireXMPP Server,双击 openfire(default)编译工程

(5)如果在Console视图中显示“BUILD SUCCESSFUL”就表示编译成功。

1.4 创建项目Builder

(1)在eclipse的菜单栏中,选择Run->Open Run Dialog...,在弹出的对话框左侧的树形结构中选择Java Application,单击右键,选择New创建启动配置。

(2) 在Run窗口的Main选项卡中, 修改Name文本框中的值,改成包含要启动的类的工程名openfire

(3)在Run窗口的Main选项卡中,点Browse按钮,选择openfire

(4)在Run窗口的Main选项卡中,点Search按钮,在弹出的对话框中输入“se”,就可以很方便地将选择Main class为org.jivesoftware.openfire.starter.ServerStarter,单击Apply按钮。(这是openfire的启动类)

(5)点击进入Arguments选项卡,在VM arguments文本框中输入

-DopenfireHome="${workspace_loc:openfire}/target/openfire"

单击Apply按钮。这个是用于eclipse执行java命令时传递的参数,这样openfire程序可以通过System.getProperty(“openfireHome”)得到openfire的本地位置。

(6)点击进入Classpath选项卡,选中User Entries,这样Advanced...就处于可用状态;点击Advanced...按钮,在AdvancedOptions页面,选择Add Folders, 选择openfire\src\i18n, 点OK按钮将这个文件夹加入到Classpath选项卡中;同样的方式把openfire\src\resources\jar文件夹也加到Classpath选项卡中。

(11)在Common选项卡中,勾选Run复选框,单击Apply按钮,单击close按钮。

1.5 运行并测试编译结果(openfire服务器)

(1)在Eclipse中启动->运行编译成功的openfire

在Eclipse菜单中,单击Run->Run(或快捷方式Ctrl+F11),正常情况应该在Console试图中出现如下所示的信息。

Adminconsole listening at:

  http://127.0.0.1:9090

  https://127.0.0.1:9091

但是,不管我怎么做,也不能出现上面的结果,总是在Eclipse的Console视图中出现如下图所示的运行结果,缺少https://127.0.0.1:9091这一项。

重要问题:但是,我编译之后没有提示任何错误,而且已经启动了HTTP SSL服务,但还是只有如上图显示的结果。同时,这时也没有出现一个像官方发布的openfire安装包安装完毕,启动openfire服务器之后弹出的那个显示信息的桌面应用程序对话框。

答案:初始化服务器的时候,刚开始启动服务器就只有9090。如果在系统启动时,已经自动启动了HTTP SSL服务,那么Openfire服务器启动的时候就不再新创建新的https服务,也就不会在服务器启动的对话框中显示相应的信息https://127.0.0.1:9091了;如果系统启动时,HTTP SSL设置的是手动启动,系统并没有启动HTTP SSL服务,这时Openfire服务器启动时就会创建新的https服务,并在服务器启动对话框中显示相应的信息https://127.0.0.1:9091。在点最后一步完成的时候做的什么事情,org.jivesoftware.openfire.container包中的AdminConsolePlugin这个插件是服务器启动的时候就加载的,它会判断在什么情况下开启9091,还有就是AdminConsolePlugin这个里面就是写的启动9090 9091端口。

public void startup() {

       restartNeeded = false;

       // Add listener for certificate events

       certificateListener = new CertificateListener();

       CertificateManager.addListener(certificateListener);

       adminPort = JiveGlobals.getXMLProperty("adminConsole.port",9090);

       adminSecurePort =JiveGlobals.getXMLProperty("adminConsole.securePort",

9091);

       adminServer = new Server();

       // Do not send Jetty info in HTTP headers

       adminServer.setSendServerVersion(false);

       // Create connector for http traffic if it's enabled.

       if (adminPort > 0) {

           Connector httpConnector = new SelectChannelConnector();

           // Listen on a specific network interface if it has been set.

           String bindInterface = getBindInterface();

           httpConnector.setHost(bindInterface);

           httpConnector.setPort(adminPort);

           adminServer.addConnector(httpConnector);

       }

       // Create a connector for https traffic if it's enabled.(如果https traffic没有启动时,创建一个httpstraffic连接)

       try {

           if (adminSecurePort > 0 &&CertificateManager.isRSACertificate

(SSLConfig.getKeyStore(), "*"))

           {

 

(2)在浏览器中测试运行情况

在Web浏览器中,输入http://127.0.0.1:9090/,如果可以出现配置的openfire的页面,就表示openfire启动成功。这时,就可以配置openfire服务器、添加用户,连接客户端进行聊天、发送文件等操作。

1.6 打包openfire编译结果

编译成功之后,可以将openfire打包,以便安装和使用。由于同时要运行数据库服务器、Eclipse和openfire,所以计算机的内存需要2G以上,这样才可以保证在打包的时候不会出现内存溢出的错误,而导致打包失败。

(1)在windows操作系统下,在Eclipse的Ant视图中直接双击installer图标,install4j就会读取你在build.xml文件里的信息、在里面完成做界面等打包工作;

(2)在Linux操作系统下,在Eclipse的Ant视图中双击installer.rpm图标,就可以将编译的结果打包成.rpm格式的安装包;

(3)如果要在Linux系统中打包成.tar.gz格式的安装文件,需要怎么操作?

 

1.7 错误解析

(1)在windows下,要启动HTTP SSL服务,而这个服务一般是没有启动的,需要手动进行启动。否则,在浏览器中就会出现如下错误:

HTTPERROR: 503SERVICE_UNAVAILABLE

RequestURI=/

 

Poweredby jetty://

出现这种错误的解决办法:在右击“我的电脑”->管理,在计算机管理对话框的服务与应用程序->服务选项中,查看HTTP SSL服务是否被禁用,开启服务后还有类似问题可能就是 openfire源码的问题。

(2)启动HTTP SSL服务之后,按照我上面的方法编译运行,在浏览器中输入http://127.0.0.1:9090,出现的如下所示的错误提示信息:

HTTP ERROR: 500

INTERNAL_SERVER_ERROR

RequestURI=/setup/index.jsp

Caused by:

java.lang.NullPointerException

       atorg.jivesoftware.admin.AdminConsole.getAppName(AdminConsole.java:122)

       atorg.jivesoftware.openfire.admin.decorators.setup_jsp._jspService(setup_jsp.java:168)

       atorg.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97)

       atjavax.servlet.http.HttpServlet.service(HttpServlet.java:820)

       atorg.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)

       atorg.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)

       atorg.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)

       atorg.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)

       atorg.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)

       atorg.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)

       atorg.mortbay.jetty.servlet.Dispatcher.include(Dispatcher.java:192)

       atcom.opensymphony.module.sitemesh.filter.PageFilter.applyDecorator(PageFilter.java:156)

       at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:59)

       atorg.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

       atorg.jivesoftware.util.LocaleFilter.doFilter(LocaleFilter.java:66)

       at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

       atorg.jivesoftware.util.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:42)

       atorg.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

       atorg.jivesoftware.admin.PluginFilter.doFilter(PluginFilter.java:70)

       atorg.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

       atorg.jivesoftware.admin.AuthCheckFilter.doFilter(AuthCheckFilter.java:99)

       at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

       atorg.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)

       atorg.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)

       at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)

       atorg.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)

       atorg.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)

       atorg.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)

       atorg.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)

       atorg.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)

       atorg.mortbay.jetty.Server.handle(Server.java:324)

       atorg.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)

       atorg.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:829)

       atorg.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514)

       at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)

       atorg.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)

       atorg.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)

       atorg.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)

Powered by Jetty://

这是因为是文件路径所导致的问题。出现这个问题的时候,可以采用手动方式将admin-sidebar.xml和openfire_i18n_en.properties这2个文件(或者openfire\src\resources\jar目录下的内容和openfire\src\i18n目录下的内容)直接复制到在openfire\bin目录下即可解决这个问题。(网友建议的一种解决方法)

如果再次出现,这样的问题,就需要将openfire目录下的你把openfire下的work和target文件都删除了,然后重新发布一个。因为web的内容不会自动更新的,只有.class会自动更新。

(3)当我使用这种方法在Windows系统下打包编译成功的openfire的时候,出现了如下错误:

-i18n:

dist.src:

 [copy] Copying 892 files to C:\Documents and Settings\liaolonglong

\workspace\openfire\target\release\openfire_src\documentation\docs\javadoc

 

BUILD FAILED

C:\Documents andSettings\liaolonglong\workspace\openfire\build\build.xml:1043:

The following erroroccurred while executing this line:

C:\Documents andSettings\liaolonglong\workspace\openfire\build\build.xml:821:

java.lang.OutOfMemoryError:Java heap space

 

Total time: 3minutes 53 seconds

问题解析:这里是内存溢出的错误,需要换一个更大的内存条,需要2G以上的内存,这样才不会出现这样的问题。

 

 

 

2. 定制功能并编译、调试、打包Openfire源代码

编译调试的环境(IDE):Eclipse 3.3.2+JDK1.6+Ant(内置)+openfire_src_2008-06-04.zip

在Windows操作系统下编译、调试openfire之前,需要确保HTTP SSL服务已经启动。否则,会出现各种错误信息。

2.1 获取Openfire源代码

从官方网站上直接下载官方发布版本的源代码openfire_src_2008-06-04.zip源代码压缩包;

2.2 创建openfire工程

将openfire_src_2008-06-04.zip解压后,目录下除了READEME.html、LICENSE.html和changelog.html三个网页文件之外,有下面四个子目录。

build目录:build目录下收录的是生成安装文件(例如:rpm)所要的一些文件,例如JRE等。

resources目录:resources目录下收录的是一些为实现国际化(i18n)和本地化的一些编码文件(例如:英文,中文,法文,德文等)。

documentation目录:documentation目录下收录的是一些关于Openfire安装和配置的信息,但最终要的是这里有Openfire开发的Javadoc。

src目录:这个src文件夹就是我们想要的Openfire源代码了,这下面又有许多文件夹,我们只要Java文件夹就好,这里面实现的Openfire的核心功能,通过它就可以调试Openfire。

如果您是做二次开发,为了按照自己的需求自定义openfire服务器端的功能,就可以采用这样方法新建工程:

(1)新建工程:File->New->Project->Java Project,单击Next,在Project name文本框中输入工程名:openfire,单击Finish

(2)解压后得到的openfire_src_3_5_1\src目录下所有内容复制到新建的工程目录workspace\openfire下

(3)在PackageExplorer视图中,右击openfire工程,选择Properties,在弹出的对话框中选择Libraries->Add JARs..,把openfire_src_3_5_1/build/lib目录下的*.jar类库全部添加进来;选择Source标签页,Add Folder..,选中src/java目录前的复选框,点击OK,点击OK按钮。

(在Eclipse中将此Java文件夹当成源代码文件夹新建工程,建立新工程后观察Openfire所import的包,发现Openfire用了许多开源工具,而这些库都在上文提到的Build目录的lib目录下,将Openfire用到的库都添加到这个新建工程的Referenced Libraries中。或者如果这样一个一个添加比较麻烦的话还有一个方法就是从你安装好的Openfire服务器目录下的lib目录中(例如:”D:\Program Files\Openfire\lib”或“/usr/local/openfire/lib”)找到openfire.jar,打开后将里面的org/jivesoftware删除,重新打包成jar并添加到这个新建工程的Referenced Libraries中,这样就可以省去了很多类似的工作。)

(4)但是这样新建的openfire项目没有打包部署,需要开发者自己完成相应的工作。

注意:新建工程之后,可能会出现一些警告信息,下面对此予以说明:

对于警告信息,是没有关系的。因为这些警告信息是使用1.5以上版本JDK时候才出现的,使用JDK1.4是不会出现这些警告信息的。因为这些代码中的有些是使用JDK 1.4编写的,而JDK1.4不支持泛型,而JDK 1.5和JDK1.6是支持泛型的。这些警告信息主要是提示将这些非泛型的代码修改为更加高级的方法——泛型,所以这些警告信息是不影响代码结果的。如果需要修改为泛型也是可以,使用泛型来处理相关的集合类。

2.3 自定义openfire服务器时,需要在Eclipse中对openfire源代码进行部署

参考网址:http://blog.csdn.net/racingtom/archive/2007/09/27/1803908.aspx

openfire的起始类为org.jivesoftware.openfire.starter.ServerStarter.java,但是直接运行此类却有问题,因为此类是针对Openfire安装包而设计的,此类的功能是将所用到的JAR文件解压并将class文件加载到虚拟机中,而我们要用的却是源代码中我们自己编译好的class文件。所以,我们需要一个新的启动类。

(1)一个简单的实现方法就是把src/java下的东西复制到我创建的java project下的src里了,并修改org.jivesoftware.openfire.starter包中ServerStarter.java类的源代码,具体如下(当然最好是与ServerStarter.java中的方法一样,用自定义的ClassLoader来将XMPPServer.class加载到虚拟机中)

packageorg.jivesoftware.openfire.starter;

importorg.jivesoftware.openfire.XMPPServer;

public class StandaloneStarter {

   public static void main(String[] args) {

       XMPPServer server = new XMPPServer();

   }

}

这样程序就可以跑起来了,最后的问题就是配置文件路径的问题。

(2)配置文件路径

如果文件路径配置不正确(即Openfire的Home没有设定或者设置不正确),就可能在运行时出现如下所示的问题:

Could not locatehome

java.io.FileNotFoundException......

 

ERROR 12114[Jive-ERR] ():

java.io.FileNotFoundException:XML properties file does not exist: openfire.xml........

在XMPPServer类中有一个locateOpenfire方法,这个方法就是设置openfireHome属性。

第1部分的代码如下:

String jiveConfigName ="conf" + File.separator + "openfire.xml";

// First, try to load itopenfireHome as a system property.

if (openfireHome == null) {

String homeProperty =System.getProperty("openfireHome");

try {

if (homeProperty != null) {

openfireHome =verifyHome(homeProperty, jiveConfigName);

}

}

catch (FileNotFoundException fe) {

// Ignore.

}

}

是在环境变量设置了Openfire的Home的情况下寻找openfire.xml文件

 

你可以更改第二部分的代码让Openfire找到Home:

// If we still don't have home,let's assume this is standalone

// and just look for home in astandard sub-dir location and verify

// by looking for the config file

if (openfireHome == null) {

try {

//修改的是下面的代码,将".."替换为其他路径了

openfireHome=verifyHome("C:\\ProgramFiles\\Openfire", jiveConfigName).getCanonicalFile();

}

catch (FileNotFoundException fe) {

// Ignore.

}

catch (IOException ie) {

// Ignore.

}

}

这部分默认是找当前文件路径,你可以修改它为你安装openfire的路径,这样问题就可以解决了。

(3)将新建的工程目录下src/web/WEB-INF/classes/openfire_init.xml导入到eclipse的查询路径里,如将src/web/WEB-INF/classes目录作为eclipse的源目录,这样openfire_init.xml自动copy到$openfire_home/classses下面,将openfire_init.xml中的openfireHome设置为$openfire_home

修改org.jivesoftware.openfire.starter.ServerStarter中的如下两个field,

private static final StringDEFAULT_LIB_DIR = "../lib";

private static final StringDEFAULT_ADMIN_LIB_DIR = "../plugins/admin/webapp/WEB-INF/lib";

改成:

private static final StringDIR_PREFIX = "$openfire_home";    // to be your own openfire_home

private static final StringDEFAULT_LIB_DIR = DIR_PREFIX + "lib";

private static final StringDEFAULT_ADMIN_LIB_DIR = DIR_PREFIX +"plugins/admin/webapp/WEB-INF/lib";

2.4 运行并测试编译结果(openfire服务器)

(1)在Eclipse中启动->运行编译成功的openfire

在Eclipse菜单中,单击Run->Run(或快捷方式Ctrl+F11),正常情况应该在Console试图中出现如下所示的信息。

Adminconsole listening at:

  http://127.0.0.1:9090

  https://127.0.0.1:9091

(2)在浏览器中测试运行情况

在Web浏览器中,输入http://127.0.0.1:9090/,如果可以出现配置的openfire的页面,就表示openfire编译、运行成功。

2.5 打包openfire编译结果

 

 

3. Openfire的socket网络连接

Openfire的socket网络连接包括:服务器和服务器之间的连接(监听在端口5269);外部组件和服务器之间的连接(监听在端口5275);多元(complex)连接(监听在端口5269);客户端和服务器的连接(监听在端口5222);和客户端通过TLS/SSL3.0和服务器的连接(监听在端口5223)。

这些连接都是通过ConnectionManager接口实现管理的,程序中对ConnectionManager接口的实现类是ConnectionManagerImpl,它是作为一个模块(Module)类加载到服务器中的。

(1)IoFilter

IoFilter为MINA的功能扩展提供了接口,它拦截所有的IO事件,并进行事件的预处理和后处理。它与Servlet中的filter机制十分相似,多个IoFilter存放在IoFilterChain中。IoFilter主要实现数据转换、事件日志、性能检测等功能。在Openfire中主要用filter这种机制来进行数据转换。

(2)ProtocolCodec Factory

Protocol Codec Factory提供了方便的Protocol支持,通过它的Encoder和Decoder,可以方便的扩展并支持各种基于Socket的网络协议,比如HTTP服务器、FTP服务器、Telnet服务器等。

要实现自己的编码/解码器(codec)只需要实现interface: ProtocolCodecFactory即可,在Openfire中实现ProtocolCodecFactory的类为XMPPCodecFactory。

(3)IoHandler

MINA中,所有的业务逻辑都有实现了IoHandler的class完成,当事件发生时,将触发IoHandler中的方法:

sessionCreated

sessionOpened

sessionClosed

sessionIdle

exceptionCaught

messageReceived

messageSent

在Openfire中客户端和服务器连接的IoHandler实现类是ClientConnectionHandler,它是从ConnectionHandler中继承来的。startClientListeners方法首先为Mian框架设置线程池,再将一个由XMPPCodecFactory作为ProtocolCodec Factory的Filter放入到FilterChain中,然后绑定到端口5222,并将ClientConnectionHandler作为IoHandler对数据进行处理。完成这些步骤后Openfire就在5222等待客户端的连接。

(4)客户端连接的处理过程

当有客户端进行连接时根据Mina框架的模式首先调用的是sessionOpened方法。

sessionOpened首先为此新连接构造了一个parser(XMLLightWeightParser),这个parser是专门给XMPPDecoder(是XMPPCodecFactory的解码器类)使用的,再创建一个Openfire的Connection类实例connection和一个StanzaHandler的实例。最后将以上的parser, connection和StanzaHandler的实例存放在Mina的session中,以便以后使用。

当有数据发送过来时,Mina框架会调用messageReceived方法

messageReceived首先从Mina的session中得到在sessionOpened方法中创建的StanzaHandler实例handler,然后从parsers中得到一个parser(如果parsers中没有可以创建一个新的实例)(注意这个parser和在sessionOpened方法中创建的parser不同,这个parser是用来处理Stanza的,而在sessionOpened方法中创建的parser是在filter中用来解码的,一句话说就是在sessionOpened方法中创建的parser是更低一层的parser)。最后将xml数据包交给StanzaHander的实例hander进行处理。

StanzaHander的实例hander处理xml数据包的过程

StanzaHander首先判断xml数据包的类型,.如果数据包以“<stream:stream”打头那么说明客户端刚刚连接,需要初始化通信(符合XMPP协议)Openfire首先为此客户端建立一个与客户端JID相关的ClientSession,而后与客户端交互协商例如是否使用SSL,是否使用压缩等问题。当协商完成之后进入正常通信阶段,则可以将xml数据包交给这个用户的ClientSession进行派送(deliever),经过派送数据包可以发送给PacketRouteImpl模块进行处理。

在PacketRouteImpl中包将进一步被细化处理。

 

 

 

 

 

 

 

 

 

 

 

 

 

第3节  Spark源代码分析研究

 

1. 获取、编译Spark源代码

官方网址:http://www.igniterealtime.org/community/docs/DOC-1040

编译调试的环境(IDE):Eclipse 3.3.2+JDK1.6+Ant(内置)+Subversive(SVN)插件

在Windows操作系统下编译、调试openfire之前,需要确保HTTP SSL服务已经启动。否则,会出现各种错误信息。

1.1 在Eclipse中安装Subversive(SVN)插件

(1)从http://subclipse.tigris.org/上下载Eclipse Subversive插件的压缩包site-1.2.4.zip;

(2)将site-1.2.4.zip压缩包解压之后的

(3)重启Eclipse,Windows->Open Perspective->Other,在弹出的Show View对话框的树形结构中,如果出现VSN一项就表示Eclipse Subversive插件安装成功。

1.2 利用SVN方式下载spark源代码

(1)打开Eclipse,Windows->Open Perspective->Other,在弹出的Show View对话框的树形结构中,选中“SVN Repository Exploring(SVN资源库)”,单击OK;

(2)这时,在Eclipse界面左边就会出现“SVN Repositories(SVN资源库)”面板,在该面板上单击鼠标右键,在弹出的菜单中选择“新建->资源库位置”,在弹出如图所示的“Repository Location(添加SVN资源库)”对话框中填入“http://svn.igniterealtime.org/svn/repos”,单击 “Finish”。

(3)在SVN Repositories面板上,展开树形结构找到相应版本spark的选项,右击spark下面的trunk项,选择“Check Out(检出为…)”,下载spark的代码。如下图所示:

(4)下载完成后,选择Window->Open Perspective->Java,在ProjectExplorer面板上,看到Spark项目,删掉它,在弹出来的对话框中选择“Do not delete contents”在工作目录下面找到spark文件夹,里面就是spark的源代码。

1.3 创建Spark项目

(1)点击Window->OpenPerspective->Java菜单

(2)在ProjectExplorer窗口中,如果有spark这个项目,把它删了,删除时,会问你要不要删除文件,选择“不要”。

(3)选择File->New->Project...,再选择Java->Java Project,在New Java Project窗口选择“Create project from existiing source”,然后把spark文件所在的文件夹加进去。

(4)在projectname中输入spark,要和文件夹的名字相同。

(5)点Finish

1.4 生成Spark

(1)点击Window->ShowView->Ant;

(2)右击Ant面板,选择Add Buildfiles;

(3)展开spark->build文件夹,选择build.xml,点击“OK”;

(4)在Ant面板,展开Spark,双击“release”,等一段时间,会提示“Build Successful”;

1.5 Create Project Builder

(1)点击Run->OpenDebug Dialog...,出现Run窗口

(2)选择JavaApplication,点击New按钮.

(3)在Main标签页,将New_configuration换成Spark或其它的这个无所谓.

(4)点击Project->Browse按钮,选择Spark,再点OK

(5)点击Mainclass->Search按钮,选择main所在的类Startup-org.jivesoftware.launcher,再点击OK按钮

(6)建议勾选Stopin main.

(7)点击Classpath标签页,选择User Entries ,使得Advanced..按钮变的可用,点击Advanced按钮。在弹出来的Advanced Options窗口,选择AddFolders,再点OK,在FolderSelection窗口,选择spark->src->resources 文件夹,点击OK

(8)选择Common标签页,勾选Debug、Run前面的框,点击Apply,再点击Close

1.6 Run/Debug

点击Run->Open Run Dialog..,在弹出的对话框选择Spark,然后点Run就行了。

 

 

 

 

第4节  飞鸽传输源代码分析研究与应用

 

1.飞鸽传书源代码概述

官方主页:http://ipmsg.org/index.html.en

飞鸽传书是一款基于TCP/UDP的在局域网之内点对点传输信息和文件的工具。飞鸽传书最早的版本由日本人Shirouzu Hiroaki 于1994年编写(http://www.ipmsg.org/ ),经过几次升级后它已经支持RSA加密。

1.1开源飞鸽传书.Net

项目主页:http://code.google.com/p/ipmessagernet/

飞鸽传书.Net 是使用C#编写,基于.Net框架运行的Windows应用程序,底层核心完全重写,更加稳定的同时更加标准。

 

 

 

2.飞鸽传输源代码研究分析结果的基本要求

(1)在本地选择并获取一个文件和文件夹存储路径的原理与源代码;尤其是对于文件夹,是否是采用“获取当前文件夹所在的目录之后,遍历该目录下的所有子文件夹与文件,从而获取该文件及其目录下所有文件夹与文件的路径?”;

(2)客户端之间创建、结束一次会话的原理、所使用的通信协议与流程图;

(3)客户端之间文件与文件夹传输的过程分析及其流程图,说明采用的是什么通信协议;

(4)在libjingle文件传输(C++类库)或Smack API(Java类库)基础上,基于P2P技术实现基于广域网环境下,客户端之间的文件与文件夹传输功能;飞鸽传书与基于XMPP协议实现的文件(夹)传输的方式有所不同:因为飞鸽传书可能需要直接获取对方的IP地址、主机名与用户名等信息,然后相互之间建立会话进行数据传输;而基于XMPP协议的文件(夹)传输首先是用户登录到服务器,客户端之间的会话是借助服务器建立的,建立会话之后,然后基于飞鸽传书传输文件与文件夹的原来、采用P2P方式来实现文件与文件夹的传输。

(5)确定文件服务器的上文件夹的存储路径(规则)之后,通过客户端将文件夹(包括该文件夹下的自文件夹与文件)上传到文件服务器的指定路径下;

(6)已知文件夹在文件服务器上的存储路径,将该文件夹(包括该文件夹下的自文件夹与文件)下载到本机指定目录下进行保存;

(7)根据FastCopy源代码(http://www.ipmsg.org/tools/fastcopy.html.en),说明复制、删除指定目录下文件与文件夹的基本原理、所使用的操作系统的类库,对实现该功能的源代码进行详细的注释;基于网络通信协议,编写一个小软件实现能够删除文件服务器上指定存储路径下的文件与文件夹(包括该文件夹下的自文件夹与文件)的软件;这里删除、复制服文件与文件夹的函数(命令)是不是与Windows操作系统下的MS-DOS命令是相同的?(Linux操作系统下的Shell命令相同?)

(8)对实现上述各个功能的源代码进行详细的注释,并使用UML建模工具画出相应的用例图、包图、类图与对象图、协作图等;

(9)注明自己参考的其他现有软件与源代码,及其来源与源文件。

注意:最终的文件服务器是运行在CentOS 5.1操作系统上的,而当前的实现是在Windows平台上的。

openfire开发参考网址:

(1)http://www.igniterealtime.org/community/docs/DOC-1092

(2)官方开发说明文档:http://www.igniterealtime.org/projects/openfire/documentation.jsp

(3)Openfire Javadoc:http://www.igniterealtime.org/builds/openfire/docs/latest/documentation

(4)http://www.igniterealtime.org/community/message/169146#169146

(5)http://blog.csdn.net/zhenyucheung/archive/2008/04/16/2298698.aspx

(6)http://phoenixtoday.blogbus.com/logs/17878527.html(为Spark客户端增加新功能)

0 0