AndroidPn

时间:2023-01-24 17:59:22

客户端的主要包说明

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 { //提过给XmppConnection的NotificationIQ 解析方式,在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框架配置信息:

AndroidPn

服务器和客户端之间的消息在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 =>XmppManager的connect方法=>XMPPConnection的connect方法
  6. 连接时,执行登陆操作,在登陆前先判别是否需要注册,注册前先判别是否有连接,详细连接过程见XmppManager类。这个登陆类似于QQ一样,它先通过认证建立连接后,然后会要求使用用户名和密码来登陆,这组用户名和密码是随机生成的,每台设备对应唯一一组。

用户会话状态的维持

  1. mina框架提供了IoSession对象,IoSession维护着连接状态,并提供读写流,可以将信息发送给用户。
  2. IoSession中会创建Connection、Stanzahandler两个对象的实例,并持有它们。
  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对象,放入特定格式的消息体,然后直接发送就可以了。

AndroidPn

发送

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

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

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

AndroidPn

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

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

客户端消息的接收

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

AndroidPn

    NotificationIQ中定义消息实体,

    NotificationIQProvider提供NotificationIQ的解析方法,

    NotificationPacketListener中对执行具体处理操作。

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

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

AndroidPn

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

AndroidPn

要解析服务器推送的某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服务器所生成的token,android设备则使用随机字符串,但会记录设备的唯一标识IMEI,通过IMEI可以保障同一设备不会重复注册。