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傳遞結束。