Openfire源码阅读(一)

本篇先分析openfire源码的主要流程,模块细节后续再继续分析;

一、简介:

Openfire是开源的实时协作服务器(RTC),它是基于公开协议XMPP(RFC-3920),并在此基础上实现了XMPP-IM(RFC-3921),扩展了IM功能,对实施协作的各种场景做较全面的考虑,如用户在线状态切换、消息订阅、通知等等,因此可以用来搭建即时通信服务器,其搭建的方法也很简易。

1、账号体系:

  • XMPP服务器的帐号基础,是域(Domain),例如:org.example.com,它在服务器配置时的时候设置,也是服务器能被访问到的域名或IP地址。客户端连接的时候,用这个域去寻找服务器。
  • JID:XMPP中,任何一个可能进行通信的实体,包括一个用户、或者一个聊天室,都需要一个JID。

用户的JID格式为:usre@domain,如:abc@org.example.com

聊天室的JID为:room@conference.domain, 如ABC@conference.org.example.com

一般情况,JID后面还会附带一个资源名(resource),指代这个客户端的来源,比如:abc@org.example.com/pc-abc,表示这个JID在一台名为pc-abc的设备上登录。这是同一个帐号能够在多个设备中登录的基础。

2、通信端口:

C-S连接的端口是5222,S-S连接的端口为5269,这些端口已经在MINA注册。

3、通信机制:

当客户端连接上XMPP服务器创建会话时,首先是建立一个TCP长连接,并在这个连接上收发XML流进行协商,协商通过后,服务端与客户端,可以通过Message、Presence、IQ这三种格式进行数据交换。

  •  Message:基本的消息发送,不要求得到响应,用于即时通信、群组、通知等;
  •  Presence:用来表明用户的状态,当用户离线或改变自己的状态时,就会在stream的上下文中插入一个Presence元素,来表明自身的状态。
  •  IQ:一种请求/响应机制,类似于Http的get请求。

4、  模块和插件(module/plugin):

在openfire里主要是module和plugin两类模块,一般情况下内部的模块都用module,对于一些功能的扩展或者第三方的开发扩展使用Plugin。官方其实也会自己写一个插件来扩展功能,说明插件还是比较灵活的。

Module的生命周期:initialize->start->stop->destroy;

二、启动过程:

 

1、 XMPPServer.start开始启动Openfire,首先会在initialize中完成全局变量openfireHome初始化以及配置文件openfire.xml文件的定位,然后加载系统全局配置JiveGlobals;

2、 接下来完成系统全局变量的初始化并构造全局对象XMPPServerInfoImpl(部分变量是从刚才的配置文件中读取的);

3、 初始化缓存对象:缓存对象有两种:单机运行时的本地缓存DefaultLocalCacheStrategy和集群环境下的集群缓存ClusteredCacheFactory,默认初始化为本地缓存DefaultLocalCacheStrategy,当从单机切换到集群,或者从集群切换到单机环境下时,缓存对象也相应的切换;

4、 verifyDataSource验证数据库连接是否能正常连接上数据库,如果连接正确则继续启动,否则退出openfire的启动;

5、 loadModules加载模块:将所有模块加载到hash表modules中(Class为Key,module实例为value),注意ConnectionManagerImpl模块是最后加载的,因为openfire采用mina作为服务器处理客户端连接的,在这个模块中进行了mina的服务器端绑定端口、连接管理、端口监听等事情,因此需要其他模块都能先启动起来以保证这个模块能正常启动;

6、 initModules和startModules会循环遍历所有的module,依次调用么每个模块的 init和start方法;

7、 最后调用pluginManager.start方法来启动PluginMonitor线程;PluginMonitor线程的主要处理:解压插件目录下所有拓展名为jar和war的插件,用loadPlugin( )装载该插件,最后通过firePluginsMonitored( )函数调用插件的监听函数。

三、Message请求处理流程:

 

上图只是一个简化后的基本流程处理图;

1、客户端连接服务器,服务器的MINA框架的业务处理Handler对象ClientConnectionhandler接收到连接请求,在sessionOpened中根据IoSession创建NIOconnection对象并绑定到Iosession上;

2、客户端发送封装好的登录协议,ClientConnectionhandler接收到后会创建ClientStanzaHandler对象,然后将接收到数据交给ClientStanzaHandler来处理:根据连接创建LocalClientSession对象并添加到PreAuthenticatedSessions(这个session管理器中存储着客户端与服务器建立的连接的session,但是还未通过登录认证的session)管理器中,然后完成账号的登录验证处理(验证完成后客户端会发送一个xmpp-bind的IQ协议,此时会将这个LocalClientSession添加到routingTable中,并从PreAuthenticatedSessions管理器中删除:routingTable存储了客户端JID和LocalClientSession的对应关系,这样就可以根据JID找到对应的LocalClientSession并将消息发送给对应的客户端);

3、客户端给另一个客户端发送chat消息时,消息先到达服务器ClientConnectionhandler,服务器先封装成协议数据包Message,然后交由PacketRouterImpl进行路由(PacketRouterImpl首先根据消息类型(message, IQ, Presence)交由对应的Router处理,这里chat会交由MessageRouter来处理,然后MessageRouter交给RoutingTableImpl来路由,RoutingTableImpl根据JID的信息决定路由给本地路由器localRoutingTable还是远程路由器remotePacketRouter来处理,本地路由器localRoutingTable根据JID找到对应的LocalClientSession),最后路由到对应的LocalClientSession,通过他将消息数据包Message发送给对应客户端;

 

上面的流程只是一个概要流程,openfire代码中具体的详细的的时序图如下:

 

1、客户端发送登录协议到服务器的ConnectionHander处理(ConnectionHander是MINA框架的业务逻辑处理Hanlder,新消息到达时触发messageReceived方法),服务器创建该session的协议分析器ClientStanzaHandler,然后将协议Message交给ClientStanzaHandler来处理;

2、ClientStanzaHandler.process根据连接信息创建LocalClientSession对象并添加到localSessionManager.PreAuthenticatedSessions管理器中(PreAuthenticatedSessions管理器中的session是还未登录认证的session),然后给客户端回复两条报文(其实就是登录过程中,服务端收到客户端第一个初始化流之后的两个应答:第一个是流回复,第二个是通知客户端进行STL协商。),接着完成登录认证;

3、登录成功后,客户端再发送一条chat聊天消息,ConnectionHander收到消息后,封装成Message协议数据包,传递给PacketRouterImpl.route方法来路由消息;

4、PacketRouterImpl.route中判断数据包类型,根据不同的数据包类型交给不同的路由器进行路由,Message消息交给messageRouter路由,presence消息交给presenceRouter路由,IQ消息交给iqRouter路由;

5、messageRouter.route判断是广播还是点对点,这里的chat是点对点,于是就交给RoutingTableImpl.routePacket来路由处理;

6、RoutingTableImpl.routePacket根据接收客户端的JID判断是本地路由还是远程计算机路由,本地路由时根据接受者的JID从LocalRoutingTable中查找出对应的LocalSession(LocalRoutingTable对象里面有个hash表routes,存储了JID和LocalSession的键值对,在客户端登录成功并发送xmpp-bind时,服务器会将Session和JID保存到这个hash表中,同时从PreAuthenticatedSessions管理器中删除该session),然后将协议数据包交给LocalSession. Deliver来处理,然后LocalSession又交给NIOConnection. Deliver来处理,最后交给MINA的IoSession.Write将数据包发送给接收客户端;

四、IQ请求处理流程:

与Message消息处理流程类似,IQ请求消息处理在交由Router组件路由之前与Message消息的处理完全相同;在服务器处理完成生成响应数据包后再交由路由器路由时的流程也与Message消息的处理完全相同;具体流程如下:

 

1、ConnectionHandler在收到IQ请求后将请求交给PacketRouterImpl.route路由时,Message消息交给messageRouter处理,IQ请求交给IQRouter来路由处理;

2、IQRouter.route中首先根据命名空间namespace来查找对应的IQHandler,然后交由对应的IQHandler.process来处理;每一个IQ协议都有其对应的namespace和IQHandler,他们注册在IQRouter. namespace2Handlers的hash变量中(这个hash表保存了namespace和IQHandler的键值对);在前面XMPPServer启动时将所有的module实例注册到一个hashMap中,其中实现了IQHandler的module又被注册到namespace2Handlers的hash表中,这样,此处的getHandler就能根据namespace查找到对应的IQHandler实例了;

3、IQHandler是一个抽象类,不同的IQ请求分别有不同的派生类实现,每个派生的IQHandler子类需要实现两个接口:getInfo返回该IQHandler的信息,包括name和namespace属性,handleIQ需要根据传入的IQPacket包进行处理,生成响应应答信息并封装成IQPacket;IQHandler.process方法生成响应应答数据包后,将其交给PacketDelivererImpl对象的deliver发送;

4、PacketDelivererImpl. Deliver又交给SocketPacketWriteHandler.process处理,接着又交给RoutingTableImpl. routePacket来处理,最后的处理流程与message消息的路由流程完全相同;

转载于:https://www.cnblogs.com/laoxia/p/8673391.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367513.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php 查询方法all,获取多条:all静态方法

查询多条数据:all( )方法all方法与前节课学习的get方法都是静态方法,可用模型类直接访问2. 源码:/*** 查找所有记录* access public* param mixed $data 主键列表或者查询条件(闭包)* param array|string $with 关联预查询* param b…

[译文]过犹不及,别再在编程中高射炮打蚊子

原文链接:Anyway,stop recommending bazookas to kill flies in programming. 众成翻译地址:过犹不及,别再在编程中高射炮打蚊子 译者注:翻译这篇吐槽的文章,主要是为了自省~日常工作中确实会犯类似的错误&#xff0…

Java中的for循环

上一章呢我们学习了一下java中的while循环和do while循环 现在我们来了解一下另外一种循环 for循环 for循环是编程语言中一种开界的循环语句,而循环语句 由循环体及循环的终止条件两部分组成,for循环其在各种编程语言中的实现与表达有所出入&#xff0…

SpringFox swagger2 and SpringFox swagger2 UI 接口文档生成与查看

依赖&#xff1a; <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version> <…

matlab期末复习资料,MATLAB期末复习习题及答案

MATLAB期末复习习题及答案13&#xff0c; ysin(x)&#xff0c;x从0到2 &#xff0c; x0.02 &#xff0c;求y的最大值、最小值、均值和标准差。(应用max,min,mean,std) 14&#xff0c; 参照课件中例题的方法&#xff0c;计算表达式z 10x3 y5e xcontour, hold on, quiver)15&…

多核可扩展计数器

到处都需要计数器&#xff0c;例如&#xff0c;查找应用程序的关键KPI&#xff0c;应用程序的负载&#xff0c;服务的请求总数&#xff0c;用于查找应用程序吞吐量的一些KPI等。 由于所有这些需求&#xff0c;并发复杂性也增加了&#xff0c;这使这个问题变得有趣。 如何实现…

三年前端,面试思考(二)

为什么还有&#xff08;二&#xff09; 没有想到上一篇 《三年前端&#xff0c;面试思考》 有这么多前端同学看到。 在评论区也有很多鼓励和质疑的声音&#xff0c;而且群里面交流的同学两天就达到了700人。 群里有同学问了很多问题&#xff0c;同时希望我再分享一些面试技巧…

51单片机auxr寄存器_MCS-51单片机有几个工作寄存器

工作寄存器有4组&#xff0c;每组都是8个工作寄存器R0~R7&#xff0c;通过PSW中的RS1、RS0两位来选择使用哪一组&#xff0c;如果不选&#xff0c;默认是选择第0组。RS1RS0组合为00时&#xff0c;选中第0组工作寄存器&#xff0c;R0~R7地址为00H~07H;RS1RS0组合为01时&#xff…

matlab中quat2angle,RPY_Euler_Quaternion_AngleAxis角度转化:Matlab、Python、Halc

RPY_Euler_Quaternion_AngleAxis角度转化&#xff1a;Matlab、Python、HalcRPY_Euler_Quaternion_AngleAxis角度转化&#xff1a;Matlab、Python、Halcon版本UR协作机器人和Franka机器人导出的位姿为angleVector&#xff0c;三个量表示&#xff0c;在Matlab中angleVector是四个…

基本注射/资格赛,范围

这是上周解决的DI / CDI基础知识的延续-在本文中&#xff0c;我将讨论基础注入&#xff0c;限定词和范围。 在上一个主题中&#xff0c;我们提供了有关DI / CDI概念的大量信息&#xff0c;我们还讨论了如何使用注释加载这些bean或类-这构成了对象的组成并创建了关于如何进行采…

100*100的 canvas 占多少内存?

题目 100*100的 canvas 占多少内存&#xff1f; 在 三年前端&#xff0c;面试思考 中提到了一个题目&#xff0c;非常有新意&#xff0c;这里分享一下当时面试的思考过程。 解题思路 其实真正的答案是多少我并不清楚&#xff0c;面试过程中面试官也不期待一个准确的答案&am…

1t硬盘怎么分区最好_这下尴尬了,电脑硬盘分区常见误区,移动硬盘分区方法...

大家买了新电脑硬盘要不要分区呢&#xff1f;像以往咱们买了新电脑一般会分4个区&#xff0c;C、D、E、F&#xff0c;方便更合理的分类使用&#xff0c;比如把工作放为D盘&#xff0c;娱乐影音放为E盘&#xff0c;游戏放为F盘&#xff0c;C盘为系统盘。不过渐渐地发现&#xff…

用Spring长轮询Tomcat

就像喜剧演员弗兰基 豪威尔 &#xff08; Frankie Howerd&#xff09;所说的“哦&#xff0c;小姐小姐” &#xff0c;但足够多的英国影射和双重诱惑&#xff0c;因为长轮询雄猫对隔壁的闷气不是某种性偏见&#xff0c;这是一种技术&#xff08;或更像是一种骇客&#xff09;由…

exchange 删除邮件

一 批量删除特定主题的邮件1.1 批量删除所有数据库中特定主题的邮件1) 群发了几封主题为“backup”的邮件&#xff1b; 2) 当前操作账号需要满足如下需求&#xff1a; a)该账号需属于Exchange Server 管理员角色以及源服务器和目标服务器的本地 Administrator组&#xff1b; b)…

js点击取消按钮关闭当前弹框_UI设计中“取消按钮”的分析详解

按钮&#xff0c;无论是在 Web 还是 App 上都被广泛地使用&#xff0c;而很少有设计师会注意到按钮当中的细节&#xff0c;导致在设计过程中出现一些低级的错误&#xff0c;使得用户在完成任务的过程中产生阻碍&#xff0c;无法顺利达成目的。在许多优秀的产品中&#xff0c;关…

MATLAB飞机大战第二版,windows程序设计——飞机大战札记(单文档文件登陆界面)...

windows程序设计——飞机大战笔记(单文档文件登陆界面)//2015/07/21/by xbw////环境 VS 2013飞机大战做的差不多了&#xff0c;闲来无事加点高大上的东西&#xff0c;关于单文档的登陆界面&#xff1b;&#xff1b;&#xff1b;界面有点丑&#xff0c;但是足够账号登陆了&#…

吸收Mockito的流利度

我最近发现自己编写了一些代码来集成两个不同的平台。 这些系统之一是基于Java的系统&#xff0c;而另一个虽然不是用Java编写的&#xff0c;却提供了Java API。 我将这些系统分别称为Foo和Bar。 在编写一行代码之前就很明显了&#xff0c;但是&#xff0c;测试最终的适配器将…

软件工程第三次作业

题目要求 最大连续子数组和&#xff08;最大子段和&#xff09; 问题&#xff1a; 给定n个整数&#xff08;可能为负数&#xff09;组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]a[i1]…a[j]的子段和的最大值。 当所给的整数均为负数时定义子段和为0&#xff0c;依此定义&a…

使用HTML5 IndexDB存储图像和文件

使用IndexedDB存储图像和文件 有一天&#xff0c;我们写了关于如何在localStorage中保存图像和文件的文章&#xff0c;它是关于我们今天可用的实用主义。 然而&#xff0c;localStorage有一些性能影响 - 我们将在稍后的博客中讨论这个问题 - 并且未来期望的方法是使用IndexedD…

Gitlab 项目上传

一&#xff0c;登陆gitab&#xff0c;新建reject Repository name: 仓库名称 Description(可选): 仓库描述介绍 Public, Private : 仓库权限&#xff08;公开共享&#xff0c;私有或指定合作者&#xff09; Initialize this repository with a README: 添加一个README.md gitig…