AndroidPn

客户端的主要包说明 

org.androidpn.client包下的文件

  1. public class Constants {  //包含静态数据  
  2. public class InvalidFormatException extends RuntimeException { // 运行时所产生的错误处理  
  3. public class LogUtil { //信息输出  
  4. public class NotificationService extends Service { //后台运行并响应来自服务器的事件推送通知服务
  5. public final class ServiceManager { //管理的notificatin服务,加载配置  
  6. public class XmppManager { // 管理客户端和服务器之间的连接  
  7. public class ConnectivityReceiver extends BroadcastReceiver { // 网络变化广播接收器  
  8. public class PersistentConnectionListener implements ConnectionListener { //连接关闭和重新连接事件监听器 XMPPManager中注册。
  9. public class PhoneStateChangeListener extends PhoneStateListener { //监听手机状态
  10. public class ReconnectionThread extends Thread {  // 用于断线重连   
  11. public class NotificationIQ extends IQ {                    // 来自服务器的推送消息
  12. public class NotificationPacketListener implements PacketListener {// NotificationIQ通知传入的数据包的接收XMPPManager中注册
  13. public class NotificationIQProvider implements IQProvider { //提过给XmppConnectionNotificationIQ 解析方式XMPPManager中注册
  14. public final class NotificationReceiver extends BroadcastReceiver { //推送通知消息的广播接收器  
  15. public class Notifier { //通知用户这里正式对消息进行处理,如发送本地通知等。  
  16. public class NotificationSettingsActivity extends PreferenceActivity { // 设置信息
  17. public class NotificationDetailsActivity extends Activity {  // 活动通知详细视图显示未使用

客户端使用的了smack.jar

做了一些修改,整个jar包解压了。smack.jar PacketReader解析消息,PacketWriter发消息,XMPPConnection负责连接管理。

发送消息

XmppManager -> XmppConnection-> PacketWriter

接收消息:

XmppConnection -> packetReader -> NotificationPacketListener -> NotificationReceiver -> Notifier

服务器端的主要包说明

原项目有4+5+15,共计24个包,另有一个default包,里面仅有一段控制台测试代码。

3.3.3.1 web相关的4个包

1. org.androidpn.server.console.api   接口

2. org.androidpn.server.console.controller   控制器

3. org.androidpn.server.console.vo   viewModel

4. org.androidpn.server.container    未用到

3.3.3.2 数据库访问相关的5个包

1.org.androidpn.server.dao     

2.org.androidpn.server.dao.hibernate      

3.org.androidpn.server.model  

4.org.androidpn.server.service 

5.org.androidpn.server.service.impl 

3.3.3.3 推送相关15个包

1. org.androidpn.server.util

    包中的类用来加载resources中的配置文件,在配置文件中可指定监听端口和ssl证书目录等属性。

  1. public class Config {  //读取配置信息  
  2. public class ConfigManager { //应用程序配置信息管理  

2. org.androidpn.server.xmpp 

    主要是包含有入口类XmppServer,这个类用来启动和停止server程序,

    包里面定义了一些异常类型。

  1. public class XmppServer {    //服务启动

3. org.androidpn.server.xmpp.auth

    认证相关的一些类。

  1. public class AuthToken {  //封装用户认证token 名字、域名  
  2. public class AuthManager {  //用户认证管理  

4.org.androidpn.server.xmpp.codec

    XMPP协议的XML文件解析包,通过这个包来进行xmpp协议数据传输的编码和解码。

  1. public class XmppCodecFactory implements ProtocolCodecFactory { //编码/解码器,用于解析XMPP消息  
  2. public class XmppDecoder extends CumulativeProtocolDecoder { //协议解码  (这里增加了心跳回送)
  3. public class XmppEncoder implements ProtocolEncoder { //协议编码  androidpn加密放在了Connection 类中

5.org.androidpn.server.xmpp.handler

    对消息的处理,我们可以针对不同的消息类型定义自己的handler

  1. public class IQAuthHandler extends IQHandler {//APP连接时认证身份
  2. public abstract class IQHandler { //抽象类,处理客户端发来的IQ消息  
  3. public class IQRegisterHandler extends IQHandler { //APP连接时处理用户注册  
  4. public class IQRosterHandler extends IQHandler {  //未使用,拉取好友列表
  5. public class PresenceUpdateHandler { //处理操作APP连接时登陆成功后更新在线状态

6.org.androidpn.server.xmpp.net

    负责维护与client之间的持久连接,并实现了一些传输方式供发送xmpp消息时使用。

  1. public class Connection { //一个XMPP连接服务器的实例  所有与客户端的消息连接都通过本类来处理
  2. public interface ConnectionCloseListener { // 连接关闭监听  
  3. public class IoBufferWriter extends Writer { //输入输出处理  
  4. public class StanzaHandler { //处理传入的XML (增加了一个方法getClientSession)
  5. public class XmppIoHandler implements IoHandler { //创建新的会话,销毁会话,并提供接收XML

7.org.androidpn.server.xmpp.presence

    只包含PresenceManager类,用来维护client的在线状态。

  1. public class PresenceManager { //它会调SessionManager用于判断某用户是否在线或获取用户当前的Presence

8.org.androidpn.server.xmpp.push

    NotificationManager类包含有向client发送消息的接口。

  1. public class NotificationManager { // 消息推送入口

9.org.androidpn.server.xmpp.router

    负责将收到的信息包发送到相应的handler进行处理,是一个路由包。

  1. public class IQRouter { //路由IQ消息到其相应的处理  
  2. public class MessageRouter { //路由Message消息到其相应的处理程序  
  3. public class PacketDeliverer { //将数据包发送到连接会话  
  4. public class PacketRouter { //处理传入的数据包,并将它们路由到其相应的处理程序  
  5. public class PresenceRouter { //路由Presence消息到其相应的处理程序  

10.org.androidpn.server.xmpp.session

    定义了用来表示持久链接的session,每个session包含一条连接的状态信息。

  1. public class ClientSession extends Session {  //会话  
  2. public abstract class Session { //会话抽象  
  3. public class SessionManager { //会话管理  

11.org.androidpn.server.xmpp.ssl

    对连接进行ssl认证的工具包。

  1. public class SSLConfig {  //ssl配置  
  2. public class SSLKeyManagerFactory { //负责产生ssl管理 实例  
  3. public class SSLTrustManagerFactory { //SSL信任管理器工厂类  

12.org.dom4j.io 

    输出输入流工具包

  1. public class XMPPPacketReader { // xml解析器  

13.org.jivesoftware.openfire.net 

  1. public class MXParser extends org.xmlpull.mxp1.MXParser { //解析器验证文件  

14.org.jivesoftware.openfire.nio

  1. public class XMLLightweightParser { //一个轻量级的XML解析器  

15.org.jivesoftware.util

  1. public class PropertyEventDispatcher { //事件调配  
  2. public interface PropertyEventListener { //事件监听  
  3. public class XMLWriter extends XMLFilterImpl implements LexicalHandler { //xml编辑 

推送实现

连接的建立和维持

服务器端

webRoot/WEB-INF/dispatcher-servlet.xml resources/spring-config.xml 内的bean应用启动时会自动实例化,而它们中的许多在实例化时都会直接或间接地调用XmppServer.getInstance()方法。从而启动服务。

 resources/spring-config.xml 中有相应的mina框架配置信息:

 

服务器和客户端之间的消息在org.androidpn.server.xmpp.net.XmppIoHandler中处理:

sessionCreated -> sessionOpened -> messageReceived - > sessionIdle -> sessionClosed

客户端

  1.  提供了三个监听器对手机网络状态或连接状态进行监听,在状态变化时启动断线或重连操作。
  2. 重连的时间间隔(秒)
    private int waiting() {
        //waiting 在每次重连会自增1
        if (waiting > 20) {
            return 600;
        }
        if (waiting > 13) {
            return 300;
        }
        return waiting <= 7 ? 10 : 60;
    }
  3. app操作中的任何一次页面切换都会判断服务的状态。
       //如果服务未启动,则启动服务
    private void startService(){
        if(NotificationService.getNotificationService() == null){
            // Start the service
            ServiceManager  serviceManager = new ServiceManager(this);
            serviceManager.setNotificationIcon(R.drawable.ic_launcher);
            serviceManager.startService();
        }
    }
  4. ServiceManager实例化时会读取配置信息。
  5. ServiceManager的startService =>XmppManagerconnect方法=>XMPPConnectionconnect方法
  6. 连接时,执行登陆操作,在登陆前先判别是否需要注册,注册前先判别是否有连接,详细连接过程见XmppManager这个登陆类似于QQ一样,它先通过认证建立连接后,然后会要求使用用户名和密码来登陆,这组用户名和密码是随机生成的,每台设备对应唯一一组。

用户会话状态的维持

  1. mina框架提供了IoSession对象,IoSession维护着连接状态,并提供读写流,可以将信息发送给用户。
  2. IoSession中会创建ConnectionStanzahandler两个对象的实例,并持有它们。
  3. Stanzahandler中又创建了androidPN新增的ClientSession对象的实例,ClientSession对象又持有Connection对象。
  4. Connection对象中持有这两个Session成员: IoSession ClientSession
  5. SessionManager对象维护着一个Map,它存有设备标识(apn_user表的username)与用户会话ClientSession的映射关系,并提供了一个单例。

消息推送

安卓消息服务器端的推送流程

  1. NotificationManager的方法被调用,依据参数得到一组设备标识。
  2. 通过设备标识在SessionManager中检索可以匹配到ClientSession
  3. 定义自己的xmpp消息格式并拼接出xml消息体。
  4. 通过ClientSession中的deliver方法,调用Connection中的deliver方法向用户发送消息。

 要定义和组装自己的xmpp消息,将适当的信息发送给客户端并便于客户端解析,需要修改的就是第3步,实例化一个IQ对象,放入特定格式的消息体,然后直接发送就可以了。

 

发送

ClientSession session = sessionManager.getSession(username);
if (session != null&&session.getPresence().isAvailable()) {
		iq.setTo(session.getAddress());
		session.deliver(iq);
 }

创建element的时候,传入的namespace要和客户端解析使用的namespace相匹配。

安卓消息服务器端的消息接收和处理

 

  1. 收到用户传输的数据时,首先经过IoFilter,完成消息的接收、编/解码等操作后。
  2. 解码操作由org.androidpn.server.xmpp.codec.XmppDecode来完成,解析完成后为每一个解码后的消息对象调用ProtocolDecoderOutput接口的write(Object)方法,将消息输出。
  3. 之后就开始消息的处理工作,XmppIoHandler messageReceived方法为起点,后续StanzaHandler -> PacketRouter,然后依据消息体的类型选择对应的路由器,如处理IQIQRouterRouter再根据packetnamespace选择对应的handler
  4. handler进行处理。

routerhandler类在androidpn中都有例子可以参考。开发中只要根据client发送消息的格式,定义自己的routerhandler类,然后在PacketRouter中注册router,在IQRouter中注册handler即可。

 

客户端消息的接收

  1. org.jivesoftware.smack.PacketReader负责消息的接收。
  2. NotificationIQNotificationIQProviderNotificationPacketListener三个类负责对收到的Notification格式的消息进行解析和处理

    

 

 

    NotificationIQ中定义消息实体,

    NotificationIQProvider提供NotificationIQ的解析方法

    NotificationPacketListener中对执行具体处理操作

    它们需要在XmppManager进行注册,代码如下:

     1. 连接成功后注册消息解析器

           

           2. 登陆成功后添加监听器:

               

要解析服务器推送的某IQ,需要实现*IQ*IQProvider两个类,然后要在XmppManager中注册,并在NotificationPacketListener提供相应的处理操作。

客户端消息的发送

发送消息:xmppManager.getConnection().sendPacket(*IQ);

具体执行数据传输的是org.jivesoftware.smack.PacketWriter

要发送某IQ到服务器,只要实现*IQ一个类就可以了,然后调用上面的方法发送到服务器。

主要的一些改动

APP中在NotificationService中增加对自身的引用

  方便更加便捷地在任意位置获取xmppManager对象,同时可以方便判别当前服务器是否已启动。

增加心跳

   APP端

   PacketWriter增加定时任务,默认每隔30秒向服务器发送一个空格,维持活跃状态,同时服务器会每隔300秒自动回复1个EchoIQ,如果app连续1000秒没有收到这个回应,则抛出连接中断异常,启动重连:XmppManager.startReconnectionThread()。心跳在登陆成功后启动,仅在超时后才会因异常而中断。

   服务器端

   设置readerIdleTime主动检查用户是否掉线,默认超时时间120秒

      spring-config.xml 文件底部添加:

        <bean id="getSessionConfig" factory-bean="ioAcceptor" factory-method="getSessionConfig">

          <property name="readerIdleTime" value="60"></property>

        </bean>

  org.androidpn.server.xmpp.codec.XmppDecode增加对客户端发送的“心跳”的处理(因为任何消息无论是否合法都会经过这里)。同时每隔300秒主动回一个“心跳”(一个EchoIQ)给安卓APP,在ClientSession中增加字段echoTime;在服务器发送消息给客户端时记录本次“心跳”的时间(connection.deliver(packet)中)。 

APP中修改重连功能

虽然存在3个监听器来监听手机网络和服务的状态,但这里存在BUG。

 XmppConnection中有3连接状态connectedauthenticatedwasAuthenticated

 在不是主动下线(连接服务器时发生异常或者收到服务器的shotdown的要求)的情况下,被动断网后XmppConnection里的连接状态始终是connectedConnectivityReceiver并没有被触发,重连操作无法正常执行,只是一路XMPP contected already,Account registered already下来,最后无法成功登陆。

 

 PersistentConnectionListener类 XmppManager中加入监听集合,

   XmppConnection在连接关闭时或读写异常时会调用:

XmppConnection.notifyConnectionError(e)->

PersistentConnectionListener.notifyConnectionError(e)->

xmppManager.startReconnectionThread();

  PacketReader,PacketWriter的shutDown中如果有操作未完成,会调用

PersistentConnectionListener.connectionClosed();

  在重连成功会调用

    XmppConnection.notifyReconnection()->

PersistentConnectionListener.reconnectionSuccessful(e)

 

 修改:

1. XmppConnection中新增了noConnected(),在开启重连线程时先将connected 改为false。

2. XmppManager.startReconnectionThread()可能会经常被触发,所以做了修改,避免开启多个重连线程,或一个重连线程启动多次。

3. 在PersistentConnectionListener内的几个事件中选择性地添加重连的调用。 

增加离线消息

添加notification表,发送消息前先将消息缓存到数据库中,在用户上线时主动读取这些消息。客户端上线的消息类型属于Presence(在org.androidpn.server.xmpp.handler.PresenceUpdateHandler中执行):

  StanzaHandler->PacketRouter->PresenceRouter->PresenceUpdateHandler

客户端增加了DeliverConfirmIQ,在收到消息后,主动通知服务器删除缓存。

服务器处理IQ的相关文件DeliverConfirmIQ,IQDeliverConfirmIQHandler。

如果该消息在2周后依然无法送达则会主动删除(在配置文件中可以修改这个有效期)。

其他主要的一些修改

1. 服务器端通过引入javaPNS2.2.jar来增加对IOS推送的支持。

2. 增加一个新的用户表,它和apn_user中的用户存在一对一的映射关系。apn_user表是消息推送的直接用户,因为消息推送是直接推送给手机设备新增的用户表的则是为了连接业务操作,实际操作中推送消息是通过app的业务用户映射到apn_user表中记录的用户设备来完成。因为用户的登陆设备是可更换,所以用户每次登陆(验证短信)都要重新绑定用户名设备标识(apn_user表中的username),就是移除旧的映射关系,建立新的映射关系,同时会记录用户的新设备类型(安卓还是苹果)。依据用户的设备类型,从而决定最终的消息发送方式。所有的发送模式都是最终获取一组apn_user表的用户数据,然后遍历它们完成消息推送操作的。

3. 关于apn_user中的数据,安卓会在socket连接成功后,依据设备的IMEI到服务器上查询是否已有记录,没有则会生成新的随机用户名和密码,并记录设备IMEI。另一个用户中的数据则是在APP账号首次登陆APP时通过访问服务器的API接口来创建,并建立与apn_user表中数据的映射关系,IOS设备可能会先自动创建一份apn_user表的数据再绑定。IOS设备的机器标识直接使用苹果APNS服务器所生成的tokenandroid设备则使用随机字符串,但会记录设备的唯一标识IMEI,通过IMEI可以保障同一设备不会重复注册。

 

posted @ 2015-05-21 18:40  随心~  阅读(1328)  评论(3)    收藏  举报