`

AndroidPn源码分析(一)

阅读更多

(一)入口

当服务器端启动的时候,控制台会打印一些log,除了spring和hibernate,mina,在最后的几行,就是androidpn的代码了,第一个是XmppServer类。

在XmppServer中,加载spring的配置文件。这貌似把spring加载配置文件给略了,反正也没有web.xml中提到的application*.xml文件。

(二)web流程

启动的时候,也加载了配置中的一些action(springmvc里面,不知道是不是也是这么叫),在页面中,点击各个连接,会执行这些action信息。最主要的是send的那个方法。

    public ModelAndView send(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        String broadcast = ServletRequestUtils.getStringParameter(request,
                "broadcast", "Y");
        String username = ServletRequestUtils.getStringParameter(request,
                "username");
        String title = ServletRequestUtils.getStringParameter(request, "title");
        String message = ServletRequestUtils.getStringParameter(request,
                "message");
        String uri = ServletRequestUtils.getStringParameter(request, "uri");

        String apiKey = Config.getString("apiKey", "");
        logger.debug("apiKey=" + apiKey);

        if (broadcast.equalsIgnoreCase("Y")) {
            notificationManager.sendBroadcast(apiKey, title, message, uri);
        } else {
            notificationManager.sendNotifcationToUser(apiKey, username, title,
                    message, uri);
        }

        ModelAndView mav = new ModelAndView();
        mav.setViewName("redirect:notification.do");
        return mav;
    }

上面红色的字体,即是执行的方法,现在开始进入主题了。

(三)服务流程

广播的代码:

    public void sendBroadcast(String apiKey, String title, String message,
            String uri) {
        log.debug("sendBroadcast()...");
        log.debug("message content:"+message); //2014.8.7 
        IQ notificationIQ = createNotificationIQ(apiKey, title, message, uri);
        for (ClientSession session : sessionManager.getSessions()) {
            if (session.getPresence().isAvailable()) {
                notificationIQ.setTo(session.getAddress());
                session.deliver(notificationIQ);
            }
        }
    }

在这里,首先是构造一个IQ类型的对象,基于xmpp协议的。

然后从服务器检查,是否有客户端存在,如果有客户端信息存在,就根据客户端的信息,将IQ发送到客户端。然后就看看客户端是怎么产生的。

(四)客户端连接

如果不知道该从哪里看起,那么可以用真机或者模拟器测试一下,看客户端连接的时候,服务器log里打印出了什么东西。

首先连接的时候,有这么一条首要记录:

<org.androidpn.server.xmpp.net.XmppIoHandler> : sessionCreated()...

好了,客户端连接的入口就是这个XmppIoHandler类了。

其实这个东西,是有配置的。在XmppServer启动的时候,加载spring-config.xml文件,在这个里面,有两个配置:

    <bean id="xmppHandler" class="org.androidpn.server.xmpp.net.XmppIoHandler" />

    <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"
        init-method="bind" destroy-method="unbind">
        <property name="defaultLocalAddress" value=":5222" />
        <property name="handler" ref="xmppHandler" />
        <property name="filterChainBuilder" ref="filterChainBuilder" />
        <property name="reuseAddress" value="true" />
    </bean>

这个是mina的,这个东西之前没有接触过。但是客户端和服务器之间的通讯连接,是基于这个框架的。百度百科有很精简的一段介绍。

XmppIoHandler这个类,实现IOHandler接口中的几个方法。关于这几个方法,需要说明一下,不然真的是一头雾水,这里引用了一下网上的资料:

Handler用来处理MINA触发的I/O事件。IoHandler是一个核心接口,它定义了Filter链末端需要的所有行为。IoHandler接口包含以下方法:

    sessionCreated  sessionOpened sessionClosed  sessionIdle  exceptionCaught  messageReceived   messageSent

sessionCreated事件
一个新的connection被创建时,会触发SessionCreated事件。对于TCP来说,这个事件代表连接的建立;对于UDP来说,它代 表收到了一个UDP数据包。这个方法可以用作初始化session的各种属性,
也可以用来在一个新建的connection上触发一些一次性的行为。 I
/O processor线程会调用这个方法,所以在实现该方法时,只加入一些耗时较少的操作,因为I/O processor线程是用来处理多会话的。
sessionOpened事件 当一个connection打开时会触发sessionOpened事件,这个事件永远在sessionCreated之后触发。如果配置了线程模式,那么这个方法会被非I
/O processor线程调用。
sessionClosed事件 当一个session关闭的时候会触发sessionClosed事件。可以将session的清理操作放在这个方法里进行。
sessionIdle事件 当一个session空闲的时候会触发sessionIdle事件。当使用UDP时该方法将不会被调用。
exceptionCaught事件 当用户代码或MINA框架抛出异常时,会触发事件事件。如果该异常是一个IOException,那么connection会被关闭。
messageReceived事件 当接收到消息的时候会触发messageReceived事件。所有的业务处理代码应该写在这里,但要留心你所要的消息类型。
messageSent事件 当消息已被远端接收到的时候,会触发messageSent事件(调用IoSession.write()发送消息)。

从服务器的后台log中也可以发现,这些事件的创建顺序:sessionCreated() -- sessionOpened() -- messageReceived() -- messageSent() --  exceptionCaught() .

在创建session这个里面,并没有做什么事情。

关于sessionopen:

    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));
    }

在sessionOpen里面,则做了一些事情。首先IoSession这个类型,从eclipse的自动补全中,可以看到里面有很多属性,包括源码中列出的该session来自的地址ip。在这个里面,作者又给它增加了几个属性:XML_PARSER,CONNECTION,STANZA_HANDLER。三个分别均为对象。关于connection属性,应该是为了服务器发送消息的时候需要的。最后一个字段按字面意思来,是处理xml节点的。总的看来,这个方法是对从客户端发来的连接信息,进行了又一次的封装,增加了一些信息。

messageReceived(),当客户端连接的时候,服务器是会有消息接收的,收到的是客户端发来的,建立连接的一条消息:

<stream:stream to="192.168.10.100" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">
    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();
        }
    }

这个方法里面,也做了一些事情。首先是拿到在sessionOpen中增加的STANZA_HANDLER对象,然后再拿到XMPP packet的解析器。下面就开始了StanzaHandler类的process方法,开始处理得到的xml信息的东西了。这个貌似是个大事件,还得擦亮枪慢慢来看,这个方法有点长,着实纠结分段看吧:

    public void process(String stanza, XMPPPacketReader reader)
            throws Exception {
        boolean initialStream = stanza.startsWith("<stream:stream");
        if (!sessionCreated || initialStream) {
            if (!initialStream) {
                return; // Ignore <?xml version="1.0"?>
            }
            if (!sessionCreated) {
                sessionCreated = true;
                MXParser parser = reader.getXPPParser();
                parser.setInput(new StringReader(stanza));
                createSession(parser);
            } else if (startedTLS) {
                startedTLS = false;
                tlsNegotiated();
            }
            return;
        }

如果发来的信息,不是以<stream:stream开头的,那直接就gg了,返回到 XmppIoHandler类的messageReceived方法,一切就over了。

里面很多的逻辑判断,如果创建了session或者所得到的节点信息是以<stream:stream开头的,这两个有一个符合条件的话,就会进入下面的判断。

客户端和服务器连接的时候,if (!sessionCreated)这个会确定执行,因为sessionCreated默认就是false。这里把客户端发来的:

<stream:stream to="192.168.10.100" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">

这段信息,放到了MXParser对象(这些是openfire官方的东西)中,然后调用了createSession方法,这个方法中有下列代码:

  // Create the correct session based on the sent namespace
        String namespace = xpp.getNamespace(null);
        if ("jabber:client".equals(namespace)) {
            session = ClientSession.createSession(serverName, connection, xpp);
            if (session == null) {
                StringBuilder sb = new StringBuilder(250);
                sb.append("<?xml version='1.0' encoding='UTF-8'?>");
                sb.append("<stream:stream from=\"").append(serverName);
                sb.append("\" id=\"").append(randomString(5));
                sb.append("\" xmlns=\"").append(xpp.getNamespace(null));
                sb.append("\" xmlns:stream=\"").append(
                        xpp.getNamespace("stream"));
                sb.append("\" version=\"1.0\">");

                // bad-namespace-prefix in the response
                StreamError error = new StreamError(
                        StreamError.Condition.bad_namespace_prefix);
                sb.append(error.toXML());
                connection.deliverRawText(sb.toString());
                connection.close();
                log
                        .warn("Closing session due to bad_namespace_prefix in stream header: "
                                + namespace);
            }
        }

原本注释里面说这个是根据发送来的命名空间,来建立正确的session。即是这句:

session = ClientSession.createSession(serverName, connection, xpp);

这个是静态类,当进入这个方法时,我们就可以看到在控制台输出的一条log信息。
这里对connection做了一些处理,设置语言还有xmpp的版本信息。然后才是真正的创建ClientSession:

 ClientSession session = SessionManager.getInstance()
                .createClientSession(connection);

 

 

 

from  http://www.cnblogs.com/juepei/p/3899784.html

分享到:
评论

相关推荐

    androidPN源码eclipse tomcat版本

    以上就是关于"androidPN源码eclipse tomcat版本"的主要知识点。了解并掌握这些内容,你就能有效地实现一个简单的推送通知服务。在实际开发过程中,可能还需要考虑安全性、性能优化以及兼容性等问题。

    AndroidPN源码

    **四、AndroidPN源码分析** AndroidPN的源码可以帮助开发者理解如何实现一个完整的推送服务。通过阅读`PushService`、`PushReceiver`、`PushServerSync`等相关类,可以学习到如何创建后台服务、如何处理广播接收器...

    Androidpn源码

    源码分析可以揭示如何实现高效、可靠的消息队列。 3. **推送服务**:服务器端有一个后台服务,定时检查消息队列并尝试发送消息。这部分可能涉及到多线程和异步处理,确保消息能够及时、准确地发送到目标设备。 4. ...

    androidpn服务端项目源码已修改

    总的来说,这个修改过的AndroidPN服务端项目源码为开发者提供了一次学习和实践推送通知服务的机会,通过分析和理解代码,可以提升Android应用的实时通信能力和后台服务的构建技能。同时,对于想要自建推送服务的...

    androidpn 消息推送 服务器端源码

    AndroidPN(Android Push Notification)是一个专门针对Android平台设计的消息推送系统,其服务器端源码的开放为开发者提供了自定义和扩展的可能性。本文将深入探讨AndroidPN服务器端源码的关键知识点,帮助开发者...

    androidpn消息推送源码

    7. **学习和实践**:分析和研究AndroidPN的源码,可以帮助开发者深入理解XMPP协议的工作原理,以及在Android平台上实现消息推送服务的具体步骤。通过修改和扩展源码,可以定制自己的推送服务,满足特定的应用场景...

    Android推送框架 androidpn

    Androidpn推送框架源码分析及配置方法,压缩包里面包括服务端和客户端代码,及说明文档

    about androidPN

    【AndroidPN】是一个开源的Android推送通知服务,它基于XMPP协议,由Java编写,提供了一种可扩展的方案,让服务器能够向Android客户端...开发者可以通过理解和定制AndroidPN源码,根据自身需求构建更个性化的推送服务。

    androidpn

    由于描述中没有给出详细信息,我们将根据标签和可能的源码分析来探讨相关知识点。 1. **Android 应用程序架构**: 在Android平台上,开发者通常使用Activity、Service、BroadcastReceiver和ContentProvider这四大...

    androidpn-client 0.5 推送

    **三、源码分析** 在"androidpn-client-0.5.01111"源码包中,我们可以找到以下几个关键模块: 1. **MainActivity**:主活动类,启动客户端并处理用户交互。 2. **PushManager**:推送管理器,负责客户端的注册、...

    androidpn-client

    在"androidpn-client"的源码中,我们可以学习到如何在Android应用中实现上述功能,包括使用Socket编程、线程管理、JSON解析、Android服务和广播接收器等技术。此外,对于希望深入理解Android推送通知机制或者想要...

    anroid完美实现 push推送 源码奉送

    3. **源码分析**: - `androidpn-client-0.5.0.zip` 包含了AndroidPN的客户端源码,开发者可以深入研究其中的网络连接、消息解析和推送处理等逻辑。 - `androidpn-server-0.5.0-src.zip` 提供了服务端的源代码,有...

    MyPushManager源码

    本文将深入剖析基于Androidpn实现的消息推送系统——MyPushManager的源码,以期帮助开发者理解并掌握这一技术。 首先,我们要明白消息推送的基本原理。Androidpn采用的是长连接机制,通过维持一个持久的TCP连接,...

    Android 基于XMPP协议IM聊天实现(客户端+服务端) 源码

    4. **源码结构分析**: "本源码使用帮助.txt"可能包含项目的基本用法、注意事项和配置步骤。"androidpn-client"应该是Android客户端的源代码,包括必要的XML配置文件、Activity、Service、BroadcastReceiver等组件...

    android客户端分析

    **二、源码分析** 1. **初始化与服务启动** 在程序的入口类`DemoAppActivity`中,首先创建了一个`ServiceManager`实例,然后调用`setNotificationIcon()`设置通知图标,并通过`startService()`启动消息接收服务。`...

    android IM

    smack 源码分析一(android上实现长连接) http://wangqinghua123.iteye.com/blog/1341355 http://www.push-notification.mobi/get_api_key.php android smack源码分析——接收消息以及如何解析消息: ...

    asmack源码(asmack-android-18-source-0.8.9)

    ASMack的主要任务是提供一个轻量级、高效的框架,使得开发者能够在Android设备上实现XMPP协议的功能,如即时通讯、群聊、推送通知(AndroidPN)等。它支持XEP扩展协议,使得开发者可以方便地添加更多的功能,例如...

    andoidpn官网服务器、客户端全部数据

    这里提到的“andoidpn官网服务器、客户端全部数据”,包含的应该是AndroidPN项目的服务器端代码、客户端应用源码以及可能的配置文件、数据库脚本等,还有提供的一个apk文件,可能是编译后的AndroidPN客户端应用。...

    Android消息推送

    本文将深入探讨Android消息推送的相关知识点,包括其工作原理、实现方式以及相关源码分析。 1. **消息推送工作原理** - **GCM/FCM**:Google Cloud Messaging已被Firebase Cloud Messaging取代,FCM是Google提供的...

    基于XMPP实现android客户端与服务器的交互(服务器、客户端源码)

    描述中的“源码”提示我们,这个压缩包可能包含了实现这一功能的服务器和客户端的源代码,这对于开发者来说是宝贵的资源,可以直接学习和修改以适应自己的项目需求。 标签“android xmpp”进一步确认了我们讨论的...

Global site tag (gtag.js) - Google Analytics