转:http://blog.csdn.net/huwenfeng_2011/article/details/43416523

下面是下部分

C2S

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

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

2、当有数据发送过来时,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进行处理。

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

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

4、PacketRouteImpl中包将进一步被细化处理

PacketRouteImp1将packet分成Message、Iq和Presence,分别交由MessageRouter、          IqRouter和PresenceRouter进行下一步路由。

5、MessageRoute处理

调用routetableImp1进行处理,然后交由通过getRoute方法获取session,最后调用           NIOConnection的deliver方法。

6、IQRoute 处理

根据IQ的不同的命名空间通过getHandler方法找到相应的iq处理方法进行处理。

注意:在IQRouter的initialize方法中,iqHandlers.addAll方法会将iq的命名空间与     其对应的处理方法存储到一个map中。

7、PresenceRoute处理

调用PresenceUpdateHandler的process方法(处理数据库的更新和缓存的更新),然后    调用Roster的boradcastPresence方法(检查privacylist(隐身及黑名单用户)然后路由给  所有在线好友),再调用routeTable的getRoutes()获取session,最后调用NIOConnection     的deliver方法。

客户端连接

管理控制台:会话->有效会话->客户端会话

会话列表在session-summary.jsp中,

  1. SessionResultFilter filter = SessionResultFilter.createDefaultSessionFilter();
  2. filter.setSortOrder(order);
  3. filter.setStartIndex(start);
  4. filter.setNumResults(range);
  5. Collection<ClientSession> sessions = sessionManager.getSessions(filter);

获取在线用户有如下步骤:

1.会得到SessionResultFilter的会话过滤器

2.然后在获取所在在线客户会话(ClientSession)。

客户端的session存在会话路由表中,通过会话路由得到所有的在线用户。

3.排序列表。当拿到客户端的在线的连接,重新迭代列表集合,通过 sort的方法排序

4.然后封装过滤后的列表返回给前端

注:在SessionManager.getSession(SessionResultFilterfiler)的方法应该还可以优化,以后在考虑

 

关闭客户端连接

点击会话列表中的可断开连接。

通过JID(admin@192.169.1.104/Spark2.6.3)从会话路由中查找会话连接。调用close方法关闭

  1. JID address = new JID(jid);
  2. try {
  3. Session sess = sessionManager.getSession(address);
  4. sess.close();
  5. // Log the event
  6. webManager.logEvent("closed session for address "+address, null);
  7. // wait one second
  8. Thread.sleep(1000L);
  9. }
  10. catch (Exception ignored) {
  11. // Session might have disappeared on its own
  12. }

S2S

控制台的配置

Openfire中Serverto Server 连接默认使用5269 端口,在管理控制台:服务器->服务器设置->服务器到服务器

当你选择正常使用的时候,后台会调用ConnectionManager中的enableServerListener()和setServerListenerPort(port);

方法流程

流程图:

在创建服务监听方法中相应的localIPAddress和端口信息被包装成一个SocketAcceptThread,做为一个独立的线程运行。在SocketAcceptThread 里面有一个BlockingAcceptingMode 成员,专门用来监听Socket连接。

代码清单:

  1. public void run() {
  2. while (notTerminated) {
  3. try {
  4. Socket sock = serverSocket.accept();
  5. if (sock != null) {
  6. Log.debug("Connect " + sock.toString());
  7. SocketReader reader =
  8. connManager.createSocketReader(sock, false, serverPort, true);
  9. Thread thread = new Thread(reader, reader.getName());
  10. thread.setDaemon(true);
  11. thread.setPriority(Thread.NORM_PRIORITY);
  12. thread.start();
  13. }
  14. }
  15. catch (IOException ie) {
  16. 。。。。。。。
  17. }
  18. catch (Throwable e) {
  19. 。。。。。。。
  20. }
  21. }
  22. }

每监听到一个连接都会交由一个线程来接管。 这个线程运行SocketReader的run()方法,接收消息,具体的消息的接收和初步处理由SocketReader里面的SocketReadingMode对象(这个对象指定是否使用阻塞和非阻塞套接字连接。)进行。SocketReadingMode反过来调用ServerSocketReader的createSession()方法根据namespace创建Session。所有其它域连接到本地服务器的session都由LocalIncomingServerSession保持,session的创建也是由它来进行。如代码清单:

  1. boolean createSession(String namespace) throws UnauthorizedException, XmlPullParserException,
  2. IOException {
  3. if ("jabber:server".equals(namespace)) {
  4. // The connected client is a server so create an IncomingServerSession
  5. session = LocalIncomingServerSession.createSession(serverName, reader, connection);
  6. return true;
  7. }
  8. return false;
  9. }

session创建时会发送相应的握手信息给对方。初始建立的session是未验证的,此时如果对端发送iq、message、presence等消息由ServerSocketReader接收,其调 用packetReceived() 方法验证域名并抛出PacketRejectedException,所以S2S开始发送的应该为验证信息。本地ServerSocketReader收到验证信息后由processUnknowPacket() 方法处理,调用LocalIncomingServerSession 的validateSubsequentDomain()验证。具体的由一个ServerDialback对象进行处理。刚建立连接时发送了握手信息,这里的ServerDialback对象则验证对方返回的key是否正确,OK的情况下则发送成功通知。验证通过的session会加入到LocalIncomingServerSession的已鉴权列表中,后面发送的iq、message、presence消息就不会再被拦截。

代码清单:

  1. public static LocalIncomingServerSession createSession(String serverName,
  2. XMPPPacketReader reader, SocketConnection connection) throws
  3. XmlPullParserException, IOException {
  4. XmlPullParser xpp = reader.getXPPParser();
  5. String version = xpp.getAttributeValue("", "version");
  6. int[] serverVersion = version != null ? decodeVersion(version) : new int[]
  7. {0,0};
  8. try {
  9. // 在新的会话中获取stream ID
  10. StreamID streamID = SessionManager.getInstance().nextStreamID();
  11. // 与远程服务器创建会话
  12. LocalIncomingServerSession session = SessionManager.getInstance().
  13. createIncomingServerSession(connection,streamID);
  14. // 发送消息流头部
  15. StringBuilder openingStream = new StringBuilder();
  16. openingStream.append("<stream:stream");
  17. openingStream.append(" xmlns:db=\"jabber:server:dialback\"");
  18. openingStream.append("xmlns:stream=\"http:
  19. //etherx.jabber.org/streams\"");
  20. openingStream.append(" xmlns=\"jabber:server\"");
  21. openingStream.append(" from=\"").append(serverName).append("\"");
  22. openingStream.append(" id=\"").append(streamID).append("\"");
  23. // OF-443: 无响应,减少对这个域的连接
  24. if (serverVersion[0] >= 1) {
  25. openingStream.append(" version=\"1.0\">");
  26. } else {
  27. openingStream.append(">");
  28. }
  29. connection.deliverRawText(openingStream.toString());
  30. if (serverVersion[0] >= 1) {
  31. // 远程服务器XMPP 1.0兼容所以提供TLS和SASL建立连接(服务器回拨)
  32. // 之时TLS策略用于此连接
  33. Connection.TLSPolicy tlsPolicy =  ServerDialback.isEnabled() ?
  34. Connection.TLSPolicy.optional :
  35. Connection.TLSPolicy.required;
  36. boolean hasCertificates = false;
  37. try {
  38. hasCertificates = SSLConfig.getKeyStore().size() > 0;
  39. }
  40. catch (Exception e) {
  41. Log.error(e.getMessage(), e);
  42. }
  43. if (Connection.TLSPolicy.required == tlsPolicy && !hasCertificates)    {
  44. Log.error("Server session rejected. TLS is required but no
  45. certificates " + "were created.");
  46. return null;
  47. }
  48. connection.setTlsPolicy(hasCertificates ? tlsPolicy :
  49. Connection.TLSPolicy.disabled);
  50. }
  51. // 显示压缩策略用于此连接
  52. String policyName =
  53. JiveGlobals.getProperty("xmpp.server.compression.policy",
  54. Connection.CompressionPolicy.disabled.toString());
  55. Connection.CompressionPolicy compressionPolicy =
  56. Connection.CompressionPolicy.valueOf(policyName);
  57. connection.setCompressionPolicy(compressionPolicy);
  58. StringBuilder sb = new StringBuilder();
  59. if (serverVersion[0] >= 1) {
  60. //远程服务器XMPP 1.0兼容所以提供TLS和SASL建立连接(服务器回拨)
  1. <span style="white-space:pre">        </span>// 不提供流特性对pre - 1.0服务器,因为它混淆他们。发送功能Openfire < 3.7.1混淆太-的- 443)
  1. <span style="white-space: pre;">          </span>sb.append("<stream:features>");
  1. if (JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true)) {
  2. sb.append("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\">");
  3. if (!ServerDialback.isEnabled()) {
  4. // 服务器回拨要求TLS关闭                        sb.append("<required/>");
  5. }
  6. sb.append("</starttls>");
  7. }
  8. // 引用可用SASL机制
  9. sb.append(SASLAuthentication.getSASLMechanisms(session));
  10. if (ServerDialback.isEnabled()) {
  11. // 当TLS不是必需的,可以提供服务器回拨
  12. fter TLS has been negotiated and a self-signed certificate is being used
  13. sb.append("<dialback xmlns=\"urn:xmpp:features:dialback\"/>");
  14. }
  15. sb.append("</stream:features>");
  16. }
  17. connection.deliverRawText(sb.toString());
  18. // Set the domain or subdomain of the local server targeted by the remote server
  19. session.setLocalDomain(serverName);
  20. return session;
  21. }
  22. catch (Exception e) {
  23. Log.error("Error establishing connection from remote server:" + connection, e);
  24. connection.close();
  25. return null;
  26. }
  27. }

S2S建立连接

发送给其它服务器的消息由@domain 部分区分,在进入到服务器路由后在

RoutingTableImpl.routePacket(Packetpacket) 中与发送给本地服务器的消息分离。

  1. public void routePacket(JID jid, Packet packet, boolean fromServer) throws PacketException {
  2. boolean routed = false;
  3. if (serverName.equals(jid.getDomain())) {
  4. // Packet sent to our domain.
  5. routed = routeToLocalDomain(jid, packet, fromServer);
  6. }
  7. else if (jid.getDomain().contains(serverName)) {
  8. // Packet sent to component hosted in this server 数据包发送到组件驻留在这个服务器
  9. routed = routeToComponent(jid, packet, routed);
  10. }
  11. else {
  12. // Packet sent to remote server
  13. routed = routeToRemoteDomain(jid, packet, routed);
  14. }
  15. if (!routed) {
  16. if (Log.isDebugEnabled()) {
  17. Log.debug("RoutingTableImpl: Failed to route packet to JID: {} packet: {}", jid, packet.toXML());
  18. }
  19. if (packet instanceof IQ) {
  20. iqRouter.routingFailed(jid, packet);
  21. }
  22. else if (packet instanceof Message) {
  23. messageRouter.routingFailed(jid, packet);
  24. }
  25. else if (packet instanceof Presence) {
  26. presenceRouter.routingFailed(jid, packet);
  27. }
  28. }
  29. }

在初次发送消息给外部服务器时两台服务器的连接还没有建立,这种情况下会将包交由一个OutgoingSessionPromise 对象来处理,将消息加入它的队列。在OutgoingSessionPromise 中保有一个线程池和一个独立线程。独立线程不断从消息队列中读取要处理的packet,并针对每个domain建立一个PacketsProcessor线程,将消息交给这个线程,然后把此线程放入线程池中运行。PacketsProcessor在发送消息包时会判断到外部服务器的连接是否已经建立。未建立的情况下会调用LocalOutgoingDServerSession.authenticateDomain()方法建立连接。具体的Socket连接建立是在authenticateDomain() 方法中经过一系列的验证和鉴权后调用createOutgoingSession(domain,hostname,port)来完成。建立好连接后则重新调用routingTable.routePacket()再进行一次路由。

(转)OpenFire源码学习之十一:连接管理(下)的更多相关文章

  1. 【Tomcat源码学习】-4.连接管理

    前面几节主要针对于Tomcat容器以及内容加载进行了讲解,本节主要针对于连接器-Connector进行细化,作为连接器主要的目的是监听外围网络访问请求,而连接器在启动相关监听进程后,是通过NIO方式进 ...

  2. Spring5.0源码学习系列之事务管理概述

    Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述 1.什么是事务? 事务就是一组原子性的SQL操作 ...

  3. (转)OpenFire源码学习之十:连接管理(上)

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43415827 关于连接管理分为上下两部分 连接管理 在大并发环境下,连接资源 需要随着用 ...

  4. (转)OpenFire源码学习之二十七:Smack源码解析

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43484199 Smack Smack是一个用于和XMPP服务器通信的类库,由此可以实现即 ...

  5. (转)OpenFire源码学习之十八:IOS离线推送

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43458213 IOS离线推送 场景: 如果您有iOS端的APP,在会话聊天的时候,用户登 ...

  6. (转)OpenFire源码学习之七:组(用户群)与花名册(用户好友)

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43413651 Group 在openfire中的gorop——组,也可以理解为共享组.什 ...

  7. (转)OpenFire源码学习之六:用户注册

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43413509 用户注册 注册流程: 1.客户端进行握手给服务端发送连接消息: <s ...

  8. (转)OpenFire源码学习之四:openfire的启动流程

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43413233 openfire启动 ServerStarter 启动流程图: 启动的总入 ...

  9. (转)即时通讯IM OpenFire源码学习之三:在Eclipse中构建源码

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43412617 源码搭建 下载地址: 地址:http://www.igniterealti ...

随机推荐

  1. nginx添加一个server

    nginx添加一个server server { listen 80; server_name dev.pccb.com; index index.html index.htm; # rewrite ...

  2. tp5 thinkphp 使用phpqrcode生成带Logo的二维码

    1 下载生成二维码类库 composer require aferrandini/phpqrcode 2 点击按钮下载 //二维码下载 public function down_qrcode() { ...

  3. 建站手册-浏览器信息:Internet Explorer 浏览器

    ylbtech-建站手册-浏览器信息:Internet Explorer 浏览器 1.返回顶部 1. http://www.w3school.com.cn/browsers/browsers_inte ...

  4. seleniumIDE command命令

    语法组成要素:command.target.value. command命令 三大类型:(action.Accessor.assertion)   操作  存储  断言 操作类型——Action 浏览 ...

  5. (转)Jupyter默认目录和默认浏览器修改

    目录 1.总结:修改Anaconda中的Jupyter Notebook默认工作路径的三种方式 # 2.Jupyter默认目录和默认浏览器修改 1.总结:修改Anaconda中的Jupyter Not ...

  6. 前端之间的url 传值

    转自:https://www.cnblogs.com/candy-Yao/p/8858166.html

  7. QTP - excel操作

    1. 以数据库的形式访问Excel 通常,我们与Excel的交互,是通过创建Excel对象的方式: Set ExcelApp = CreateObject("Excel.Applicatio ...

  8. XStream教程

    XStream是一个简单的基于Java库,Java对象序列化到XML,反之亦然(即:可以轻易的将Java对象和xml文档相互转换). 特点 使用方便 - XStream的API提供了一个高层次外观,以 ...

  9. UVA10118_Free Candies状态压缩

    这题大概题意是,有四列糖果,一个人手中最多拿五个水果,每次拿水果只能从每一列最上面开始拿. 而如果手中的糖果相同就会成对抵消,奖励给玩家 问玩家怎样取能取到最多的糖果,并输出对数 这题是运用动态规划, ...

  10. Module not found: Error: Can't resolve '@babel/runtime/helpers/classCallCheck' and Module not found: Error: Can't resolve '@babel/runtime/helpers/defineProperty'

    These two mistakes are really just one mistake, This is because the following file @babel/runtime ca ...