Androidpn 实现 PUSH 推送
项目中的推送模块的原型为名为androidpn的开源项目.所以与androidpn相同,其内部使用asmack来实现xmpp协议的解析和拓展,使用MINA框架来进行多线程的socket管理。
1、当客户端安装应用后,会根据xmpp协议(这里是注册信息)通过长连接在服务器端进行注册绑定.
2、当服务器端与客户端完成注册后,会建立起相应的session(会话),这个session是维护长连接的很重要的介质.服务器端通过此管理客户端状态.
3、当服务器需要对活动用户发送消息时,会对存活的session通过socket连接发送消息。客户端会根据xmpp协议对消息进行解析,而后最终显示给用户.
其实这个androidpn主要由 MINA+XMPP实现,下载androidpn-server-0.5.0的代码部分你会发现:
NioSocketAcceptor 是消息服务器的主类(查看使用请猛击这里),当然和使用普通socket一样,设置下通信端口5222,以及Filter和Handler.这两个概念是mina的核心实现,同样这也是消息服务器的重点实现。
首先说下Filter,在mina中filter同WEB开发中的Servlet filter,你可以把它想象成串的形式,一个filter挨着一个filter.他在mima中已经实现了很多事情,
比如:
使用LoggingFilter记录发送的数据内容。
使用ProtocolCodecFilter进行二进制内容或者POJO的对象传输。
使用CompressionFilter对数据内容进行压缩。
用SSLFilter对传输数据进行加密。
这些都是filter可以做的事情,在消息服务器中,他只是简单的指定了两个filter,ExecutorFilter用于对多线程进行管理,ProtocolCodecFilter 用于对数据指定传送格式(XML)以及编码(utf-8)。
如图,Handler处在mina架构的最下方,当过滤器执行完毕后,会交由Handler去处理相应。
Handler类主要包括如下几个方法
SESSIONCREATED
SESSIONOPENED
SESSIONCLOSED
SESSIONIDLE
EXCEPTIONCAUGHT
MESSAGERECEIVED
MESSAGESENT
这里的SESSION你可以把它看做connection,即一个与服务器端的连接。这里的Handler主要处理连接的各种状态,以及发送接收消息的处理事件。
服务器的主类,由于其使用了mina的架构设计,所以在服务端启动前需要加载一些处理的Handler,在common-end.xml配置了解码类 XmppDecoder 和 XmppEncoder.以及最重要的处理类 XmppIoHandler.
XmppIoHandler负责处理了与客户端的交互部分。
比如连接建立时以及出错时只打印日志,
/** * Invoked from an I/O processor thread when a new connection has been created. */ public void sessionCreated(IoSession session) throws Exception { log.debug("sessionCreated()..."); } /** * Invoked when any exception is thrown. */ public void exceptionCaught(IoSession session, Throwable cause) throws Exception { log.debug("exceptionCaught()..."); log.error(cause); }
/** * Invoked when a connection is closed. */ public void sessionClosed(IoSession session) throws Exception { log.debug("sessionClosed()..."); Connection connection = (Connection) session.getAttribute(CONNECTION); connection.close(); } /** * Invoked with the related IdleStatus when a connection becomes idle. */ public void sessionIdle(IoSession session, IdleStatus status) throws Exception { log.debug("sessionIdle()..."); Connection connection = (Connection) session.getAttribute(CONNECTION); if (log.isDebugEnabled()) { log.debug("Closing connection that has been idle: " + connection); } connection.close(); }
/** * Invoked when a connection has been opened. */ public void sessionOpened(IoSession session) throws Exception { log.debug("sessionOpened()..."); log.debug("remoteAddress=" + session.getRemoteAddress()); // Create a new XML parser XMLLightweightParser parser = new XMLLightweightParser("UTF-8"); session.setAttribute(XML_PARSER, parser); // Create a new connection Connection connection = new Connection(session); session.setAttribute(CONNECTION, connection); session.setAttribute(STANZA_HANDLER, new StanzaHandler(serverName, connection)); }
/** * Invoked when a message is received. */ public void messageReceived(IoSession session, Object message) throws Exception { log.debug("messageReceived()..."); log.debug("RCVD: " + message); // Get the stanza handler StanzaHandler handler = (StanzaHandler) session.getAttribute(STANZA_HANDLER); // Get the XMPP packet parser int hashCode = Thread.currentThread().hashCode(); XMPPPacketReader parser = parsers.get(hashCode); if (parser == null) { parser = new XMPPPacketReader(); parser.setXPPFactory(factory); parsers.put(hashCode, parser); } // The stanza handler processes the message 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(); } } /** * Invoked when a message written by IoSession.write(Object) is sent out. */ public void messageSent(IoSession session, Object message) throws Exception { log.debug("messageSent()..."); }
<stream:stream to="192.168.0.68" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.1"><?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="127.0.0.1" id="deb12279" xml:version="1.0"><stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><auth xmlns="http://jabber.org/features/iq-auth"/><register xmlns="http://jabber.org/features/iq-register"/></stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/><iq id="xfP9F-0" type="set"><query xmlns="jabber:iq:register"><password>354316034600157</password><username>354316034600157</username></query></iq><iq type="result" id="xfP9F-0" to="127.0.0.1/deb12279"/><iq id="xfP9F-1" type="get"><query xmlns="jabber:iq:auth"><username>354316034600157</username></query></iq><iq type="result" id="xfP9F-1"><query xmlns="jabber:iq:auth"><username>354316034600157</username><password/><digest/><resource/></query></iq><iq id="xfP9F-2" type="set"><query xmlns="jabber:iq:auth"><username>354316034600157</username><digest>a77d56f44a572aef5414446ba473cbfd7ba5fb41</digest><resource>AndroidpnClient</resource></query></iq><iq type="result" id="xfP9F-2" to="354316034600157@127.0.0.1/AndroidpnClient"/><iq id="xfP9F-3" type="get"><query xmlns="jabber:iq:roster" ></query></iq><presence id="xfP9F-4"></presence>