XMPP协议学习笔记五(Openfire消息处理流程) - nomousewch的专栏 - 博客频道 - CSDN.NET
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());
- }
- }
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);
- }
- }
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();
- }
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传递结束。