XMPP协议学习笔记五(Openfire消息处理流程)

本文详细解析了Openfire服务器如何通过启动基于TCP/IP的监听服务接收客户端XML流文件,以及如何利用Mina框架处理这些文件,重点介绍了SocketAcceptor的配置和过滤器XMPPCodeFactory的作用,最后阐述了如何通过StanzaHandler解析并封装消息包。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     XMPP协议作为一个IM,其核心在于消息的传递,在Openfire服务器对XMPP的实现中,消息被封装为Packet对象,因此Openfire服务器的核心代码是对客户端Packet对象的监听和处理流程,我们今天就来研究一下Openfire的消息包接受处理流程。

    

  • 首先,Openfire服务器需要启动一个基于TCP/IP的监听服务,用以接收客户端传过来的XML流文件。这个过程在XMPPServer类的start()方法中进行,这个监听服务是以loadModule(ConnectionManagerImpl.class.getName())来加载,调用ConnectionManagerImpl类的createClientListeners()方法
    private void createClientListeners() {
        // Start clients plain socket unless it's been disabled.
        if (isClientListenerEnabled()) {
            // Create SocketAcceptor with correct number of processors
            socketAcceptor = buildSocketAcceptor();
            // Customize Executor that will be used by processors to process incoming stanzas
            ExecutorThreadModel threadModel = ExecutorThreadModel.getInstance("client");
            int eventThreads = JiveGlobals.getIntProperty("xmpp.client.processing.threads", 16);
            ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)threadModel.getExecutor();
            eventExecutor.setCorePoolSize(eventThreads + 1);
            eventExecutor.setMaximumPoolSize(eventThreads + 1);
            eventExecutor.setKeepAliveTime(60, TimeUnit.SECONDS);

            socketAcceptor.getDefaultConfig().setThreadModel(threadModel);
            // Add the XMPP codec filter
            socketAcceptor.getFilterChain().addFirst("xmpp", new ProtocolCodecFilter(new XMPPCodecFactory()));
            // Kill sessions whose outgoing queues keep growing and fail to send traffic
            socketAcceptor.getFilterChain().addAfter("xmpp", "outCap", new StalledSessionsFilter());
        }
    }
  • 其中的socketAcceptor是在buildSocektAcceptor()方法中定义的,它是作为一个服务端的接收器,是mina框架为我们封装好的一个socketserver,在上面这个方法中,我们为socketAcceptor添加了一个过滤器,XMPPCodeFactory,这个类将过滤xmpp相关请求,加以处理,我们再看同一个类的另外一个方法startClientListener()
     private void startClientListeners(String localIPAddress) {
        // Start clients plain socket unless it's been disabled.
        if (isClientListenerEnabled()) {
            int port = getClientListenerPort();
            try {
                // Listen on a specific network interface if it has been set.
                String interfaceName = JiveGlobals.getXMLProperty("network.interface");
                InetAddress bindInterface = null;
                if (interfaceName != null) {
                    if (interfaceName.trim().length() > 0) {
                        bindInterface = InetAddress.getByName(interfaceName);
                    }
                }
                // Start accepting connections
                socketAcceptor
                        .bind(new InetSocketAddress(bindInterface, port), new ClientConnectionHandler(serverName));

                ports.add(new ServerPort(port, serverName, localIPAddress, false, null, ServerPort.Type.client));

                List<String> params = new ArrayList<String>();
                params.add(Integer.toString(port));
                Log.info(LocaleUtils.getLocalizedString("startup.plain", params));
            }
            catch (Exception e) {
                System.err.println("Error starting XMPP listener on port " + port + ": " +
                        e.getMessage());
                Log.error(LocaleUtils.getLocalizedString("admin.error.socket-setup"), e);
            }
        }
    }
  • 其中的socketAcceptor.bind()方法启动了监听服务器,来监听所有发送到服务器5222端口的数据,并用ClientConnetionHandler类来处理,ClinetConnectionHandler继承于ConnectionHandler类,后者实现了mina的IoHandlerAdaptor接口,其中的messageReceived()方法是关键。
        public void messageReceived(IoSession session, Object message) throws Exception {
        // Get the stanza handler for this session
        StanzaHandler handler = (StanzaHandler) session.getAttribute(HANDLER);
        // Get the parser to use to process stanza. For optimization there is going
        // to be a parser for each running thread. Each Filter will be executed
        // by the Executor placed as the first Filter. So we can have a parser associated
        // to each Thread
        int hashCode = Thread.currentThread().hashCode();
        XMPPPacketReader parser = parsers.get(hashCode);
        if (parser == null) {
            parser = new XMPPPacketReader();
            parser.setXPPFactory(factory);
            parsers.put(hashCode, parser);
        }
        // Update counter of read btyes
        updateReadBytesCounter(session);
        //System.out.println("RCVD: " + message);
        // Let the stanza handler process the received stanza
        try {
            handler.process((String) message, parser);
        } catch (Exception e) {
            Log.error("Closing connection due to error while processing message: " + message, e);
            Connection connection = (Connection) session.getAttribute(CONNECTION);
            connection.close();
        }
    }
  • 可以看到收到的信息交由StanzaHandler的process方法中进行XML解析并封装为packet对象,然后再进行下一步的处理,至此,从客户端到服务器端的packet传递结束。


评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值