openfire 接受消息流程

56 篇文章 0 订阅

 

openfire底层采用了MINA框架,它是采用事件监听的方式,其中IoHandler接口定义了不同的事件类型,因此根据不同的事件类型做相关的处理

 

Apache MINA 是一个网络应用框架,有助于用户非常方便地开发高性能、高伸缩性的网络应用。它通过Java NIO提供了一个抽象的、事件驱动的、异步的位于各种传输协议(如TCP/IP和UDP/IP)之上的API,Apache MINA 通常可被称之为:

NIO 框架库;

客户端/服务器框架库;

或者一个网络socket库。

 

Apache MINA 是一个网络应用程序框架,它对Java中的socket和NIO进行了有效和清晰的封装,方便开发人员开发TCP/UDP程序,从而抛开在使用原始的socket时需要考虑的各种繁杂而又烦人问题(线程、性能、会话等),把更多精力专著在应用中的业务逻辑的开发上

 

Java代码   收藏代码
  1. public interface IoHandler  
  2. {  
  3. //创建session  
  4.     public abstract void sessionCreated(IoSession iosession)  
  5.         throws Exception;  
  6. //开启session  
  7.     public abstract void sessionOpened(IoSession iosession)  
  8.         throws Exception;  
  9. //关闭session  
  10.     public abstract void sessionClosed(IoSession iosession)  
  11.         throws Exception;  
  12. //session空闲  
  13.     public abstract void sessionIdle(IoSession iosession, IdleStatus idlestatus)  
  14.         throws Exception;  
  15. //异常处理  
  16.     public abstract void exceptionCaught(IoSession iosession, Throwable throwable)  
  17.         throws Exception;  
  18. //接收消息  
  19.     public abstract void messageReceived(IoSession iosession, Object obj)  
  20.         throws Exception;  
  21. //发送消息  
  22.     public abstract void messageSent(IoSession iosession, Object obj)  
  23.         throws Exception;  
  24. }  

 

抽象类ConnectionHandler继承了IoHandlerAdapter类,而IoHandlerAdapter实现了IoHandler接口:

public abstract class ConnectionHandler extends IoHandlerAdapter 

 

下面是ConnectionHandler类实现关于messageReceived事件的实现方法

Java代码   收藏代码
  1. @Override  
  2. public void messageReceived(IoSession session, Object message) throws Exception {  
  3.     // Get the stanza handler for this session  
  4.     //得到当前会话的StanzaHandler,这个对象在sessionOpened事件对应的方法中已经创建了,可以参考sessionOpened()的实现  
  5.     StanzaHandler handler = (StanzaHandler) session.getAttribute(HANDLER);  
  6.     // Get the parser to use to process stanza. For optimization there is going  
  7.     // to be a parser for each running thread. Each Filter will be executed  
  8.     // by the Executor placed as the first Filter. So we can have a parser associated  
  9.     // to each Thread  
  10.     int hashCode = Thread.currentThread().hashCode();  
  11.     XMPPPacketReader parser = parsers.get(hashCode);  
  12.     if (parser == null) {  
  13.         parser = new XMPPPacketReader();  
  14.         parser.setXPPFactory(factory);  
  15.         parsers.put(hashCode, parser);  
  16.     }  
  17.     // Update counter of read btyes  
  18.     updateReadBytesCounter(session);  
  19.     //System.out.println("RCVD: " + message);  
  20.     // Let the stanza handler process the received stanza  
  21.     try {  
  22.     //处理接收的message,parser为XMPPPacketReader对象,用来解析XML字符串,因为openfire信息之间的传递全部都是XML格式的字符串(XMPP协议)  
  23.     //下面方法的实现参考StanzaHandler.process(String stanza, XMPPPacketReader reader)方法  
  24.         handler.process((String) message, parser);  
  25.     } catch (Exception e) {  
  26.         Log.error("Closing connection due to error while processing message: " + message, e);  
  27.         Connection connection = (Connection) session.getAttribute(CONNECTION);  
  28.         connection.close();  
  29.     }  
  30. }  
  31.   
  32.   
  33.  @Override  
  34. public void sessionOpened(IoSession session) throws Exception {  
  35.     // Create a new XML parser for the new connection. The parser will be used by the XMPPDecoder filter.  
  36.     final XMLLightweightParser parser = new XMLLightweightParser(CHARSET);  
  37.     session.setAttribute(XML_PARSER, parser);  
  38.     // Create a new NIOConnection for the new session  
  39.     final NIOConnection connection = createNIOConnection(session);  
  40.     session.setAttribute(CONNECTION, connection);  
  41.     //createStanzaHandler方法是当前类的一个抽象类,具体实现需要查看ConnectionHandler的子类是如何实现的,  
  42.     session.setAttribute(HANDLER, createStanzaHandler(connection));  
  43.     // Set the max time a connection can be idle before closing it. This amount of seconds  
  44.     // is divided in two, as Openfire will ping idle clients first (at 50% of the max idle time)  
  45.     // before disconnecting them (at 100% of the max idle time). This prevents Openfire from  
  46.     // removing connections without warning.  
  47.     final int idleTime = getMaxIdleTime() / 2;  
  48.     if (idleTime > 0) {  
  49.         session.setIdleTime(IdleStatus.READER_IDLE, idleTime);  
  50.     }  
  51. }  

 

下面我就以ClientConnectionHandler类作为例子来讲解

Java代码   收藏代码
  1. public class ClientConnectionHandler extends ConnectionHandler  
  2.   
  3. public ClientStanzaHandler(PacketRouter router, String serverName, Connection connection) {  
  4.     super(router, serverName, connection);  
  5. }  
  6.   
  7. @Override  
  8. StanzaHandler createStanzaHandler(NIOConnection connection) {  
  9.     return new ClientStanzaHandler(XMPPServer.getInstance().getPacketRouter(), serverName, connection);  
  10. }  

 

StanzaHandler类:A StanzaHandler is the main responsible for handling incoming stanzas.

Java代码   收藏代码
  1. public void process(String stanza, XMPPPacketReader reader) throws Exception {  
  2.   
  3.     boolean initialStream = stanza.startsWith("<stream:stream") || stanza.startsWith("<flash:stream");  
  4.     if (!sessionCreated || initialStream) {  
  5.         if (!initialStream) {  
  6.             // Allow requests for flash socket policy files directly on the client listener port  
  7.             if (stanza.startsWith("<policy-file-request/>")) {  
  8.                 String crossDomainText = FlashCrossDomainServlet.CROSS_DOMAIN_TEXT +  
  9.                         XMPPServer.getInstance().getConnectionManager().getClientListenerPort() +  
  10.                         FlashCrossDomainServlet.CROSS_DOMAIN_END_TEXT + '\0';  
  11.                 connection.deliverRawText(crossDomainText);  
  12.                 return;  
  13.             }  
  14.             else {  
  15.                 // Ignore <?xml version="1.0"?>  
  16.                 return;  
  17.             }  
  18.         }  
  19.         // Found an stream:stream tag...  
  20.         if (!sessionCreated) {  
  21.             sessionCreated = true;  
  22.             MXParser parser = reader.getXPPParser();  
  23.             parser.setInput(new StringReader(stanza));  
  24.             createSession(parser);  
  25.         }  
  26.         else if (startedTLS) {  
  27.             startedTLS = false;  
  28.             tlsNegotiated();  
  29.         }  
  30.         else if (startedSASL && saslStatus == SASLAuthentication.Status.authenticated) {  
  31.             startedSASL = false;  
  32.             saslSuccessful();  
  33.         }  
  34.         else if (waitingCompressionACK) {  
  35.             waitingCompressionACK = false;  
  36.             compressionSuccessful();  
  37.         }  
  38.         return;  
  39.     }  
  40.   
  41.     // Verify if end of stream was requested  
  42.     if (stanza.equals("</stream:stream>")) {  
  43.         session.close();  
  44.         return;  
  45.     }  
  46.     // Ignore <?xml version="1.0"?> stanzas sent by clients  
  47.     if (stanza.startsWith("<?xml")) {  
  48.         return;  
  49.     }  
  50.     // Create DOM object from received stanza  
  51.     Element doc = reader.read(new StringReader(stanza)).getRootElement();  
  52.     if (doc == null) {  
  53.         // No document found.  
  54.         return;  
  55.     }  
  56.     String tag = doc.getName();  
  57.     if ("starttls".equals(tag)) {  
  58.         // Negotiate TLS  
  59.         if (negotiateTLS()) {  
  60.             startedTLS = true;  
  61.         }  
  62.         else {  
  63.             connection.close();  
  64.             session = null;  
  65.         }  
  66.     }  
  67.     else if ("auth".equals(tag)) {  
  68.         // User is trying to authenticate using SASL  
  69.         startedSASL = true;  
  70.         // Process authentication stanza  
  71.         saslStatus = SASLAuthentication.handle(session, doc);  
  72.     }  
  73.     else if (startedSASL && "response".equals(tag)) {  
  74.         // User is responding to SASL challenge. Process response  
  75.         saslStatus = SASLAuthentication.handle(session, doc);  
  76.     }  
  77.     else if ("compress".equals(tag)) {  
  78.         // Client is trying to initiate compression  
  79.         if (compressClient(doc)) {  
  80.             // Compression was successful so open a new stream and offer  
  81.             // resource binding and session establishment (to client sessions only)  
  82.             waitingCompressionACK = true;  
  83.         }  
  84.     }  
  85.     else {  
  86.     //最终处理消息,doc就是发送过来的XML字符串转化为Element对象  
  87.         process(doc);  
  88.     }  
  89. }  
  90.   
  91.   
  92. private void process(Element doc) throws UnauthorizedException {  
  93.     if (doc == null) {  
  94.         return;  
  95.     }  
  96.   
  97.     // Ensure that connection was secured if TLS was required  
  98.     if (connection.getTlsPolicy() == Connection.TLSPolicy.required &&  
  99.             !connection.isSecure()) {  
  100.         closeNeverSecuredConnection();  
  101.         return;  
  102.     }  
  103.   
  104.     String tag = doc.getName();  
  105.     //消息类型是<message>打头的,表示消息是message类型,这个就是对XMPP协议的解释  
  106.     if ("message".equals(tag)) {  
  107.         Message packet;  
  108.         try {  
  109.             packet = new Message(doc, !validateJIDs());  
  110.         }  
  111.         catch (IllegalArgumentException e) {  
  112.             Log.debug("Rejecting packet. JID malformed", e);  
  113.             // The original packet contains a malformed JID so answer with an error.  
  114.             Message reply = new Message();  
  115.             reply.setID(doc.attributeValue("id"));  
  116.             reply.setTo(session.getAddress());  
  117.             reply.getElement().addAttribute("from", doc.attributeValue("to"));  
  118.             reply.setError(PacketError.Condition.jid_malformed);  
  119.             session.process(reply);  
  120.             return;  
  121.         }  
  122.         processMessage(packet);  
  123.     }  
  124.     //消息类型是<presence>打头的,表示消息请求用户的状态  
  125.     else if ("presence".equals(tag)) {  
  126.         Presence packet;  
  127.         try {  
  128.             packet = new Presence(doc, !validateJIDs());  
  129.         }  
  130.         catch (IllegalArgumentException e) {  
  131.             Log.debug("Rejecting packet. JID malformed", e);  
  132.             // The original packet contains a malformed JID so answer an error  
  133.             Presence reply = new Presence();  
  134.             reply.setID(doc.attributeValue("id"));  
  135.             reply.setTo(session.getAddress());  
  136.             reply.getElement().addAttribute("from", doc.attributeValue("to"));  
  137.             reply.setError(PacketError.Condition.jid_malformed);  
  138.             session.process(reply);  
  139.             return;  
  140.         }  
  141.         // Check that the presence type is valid. If not then assume available type  
  142.         try {  
  143.             packet.getType();  
  144.         }  
  145.         catch (IllegalArgumentException e) {  
  146.             Log.warn("Invalid presence type", e);  
  147.             // The presence packet contains an invalid presence type so replace it with  
  148.             // an available presence type  
  149.             packet.setType(null);  
  150.         }  
  151.         // Check that the presence show is valid. If not then assume available show value  
  152.         try {  
  153.             packet.getShow();  
  154.         }  
  155.         catch (IllegalArgumentException e) {  
  156.             Log.warn("Invalid presence show for -" + packet.toXML(), e);  
  157.             // The presence packet contains an invalid presence show so replace it with  
  158.             // an available presence show  
  159.             packet.setShow(null);  
  160.         }  
  161.         if (session.getStatus() == Session.STATUS_CLOSED && packet.isAvailable()) {  
  162.             // Ignore available presence packets sent from a closed session. A closed  
  163.             // session may have buffered data pending to be processes so we want to ignore  
  164.             // just Presences of type available  
  165.             Log.warn("Ignoring available presence packet of closed session: " + packet);  
  166.             return;  
  167.         }  
  168.         processPresence(packet);  
  169.     }  
  170.     //消息类型是<iq>打头的,表示客户端对server端的一个请求  
  171.     else if ("iq".equals(tag)) {  
  172.         IQ packet;  
  173.         try {  
  174.             packet = getIQ(doc);  
  175.         }  
  176.         catch (IllegalArgumentException e) {  
  177.             Log.debug("Rejecting packet. JID malformed", e);  
  178.             // The original packet contains a malformed JID so answer an error  
  179.             IQ reply = new IQ();  
  180.             if (!doc.elements().isEmpty()) {  
  181.                 reply.setChildElement(((Element)doc.elements().get(0)).createCopy());  
  182.             }  
  183.             reply.setID(doc.attributeValue("id"));  
  184.             reply.setTo(session.getAddress());  
  185.             if (doc.attributeValue("to") != null) {  
  186.                 reply.getElement().addAttribute("from", doc.attributeValue("to"));  
  187.             }  
  188.             reply.setError(PacketError.Condition.jid_malformed);  
  189.             session.process(reply);  
  190.             return;  
  191.         }  
  192.         if (packet.getID() == null && JiveGlobals.getBooleanProperty("xmpp.server.validation.enabled"false)) {  
  193.             // IQ packets MUST have an 'id' attribute so close the connection  
  194.             StreamError error = new StreamError(StreamError.Condition.invalid_xml);  
  195.             session.deliverRawText(error.toXML());  
  196.             session.close();  
  197.             return;  
  198.         }  
  199.         processIQ(packet);  
  200.     }  
  201.     //如果消息类型不是IQ\Presence\Message三种类型,则执行processUnknowPacket()方法。  
  202.     else {  
  203.     //abstract boolean processUnknowPacket(Element doc) throws UnauthorizedException;  
  204.     //该方法是一个抽象类,具体的实现是要看继承ConnectionHandler类的具体类,它会重写createStanzaHandler方法。  
  205.     //在ClientConnectionHandler类中实现该方法对象是ClientStanzaHandler类  
  206.         if (!processUnknowPacket(doc)) {  
  207.             Log.warn(LocaleUtils.getLocalizedString("admin.error.packet.tag") +  
  208.                     doc.asXML());  
  209.             session.close();  
  210.         }  
  211.     }  
  212. }  

 

openfire之所以能够做到“即时通信”的目的正是因为MINA框架对socket进行了一层封装,说白了还是socket通信。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现消息的接收,需要使用Openfire提供的XMPP协议和Smack API。 首先,需要在代码中创建一个XMPP连接,连接到Openfire服务器。可以使用Smack API提供的XMPPTCPConnection类来创建连接,并传入Openfire服务器的IP地址、端口号和用户名密码等信息。 ```java XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder() .setServiceName(serverName) .setHost(serverIP) .setPort(serverPort) .setUsernameAndPassword(username, password) .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) .setCompressionEnabled(false); AbstractXMPPConnection connection = new XMPPTCPConnection(configBuilder.build()); ``` 然后,需要注册一个消息监听器,以便在收到消息时进行处理。可以使用Smack API提供的StanzaListener接口来实现监听器。 ```java connection.addAsyncStanzaListener(new StanzaListener() { @Override public void processStanza(Stanza stanza) throws SmackException.NotConnectedException, InterruptedException { if (stanza instanceof Message) { Message message = (Message) stanza; String fromUser = message.getFrom().toString(); String messageBody = message.getBody(); // 处理收到的消息 } } }, new StanzaFilter() { @Override public boolean accept(Stanza stanza) { return stanza instanceof Message; } }); ``` 最后,需要调用连接对象的connect()方法和login()方法,建立连接并登录到Openfire服务器。 ```java connection.connect(); connection.login(); ``` 这样,当有消息发送到当前用户时,就会触发消息监听器的processStanza()方法,从而实现消息的接收。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值