Smack PacketReader 启动过程分析

17 篇文章 0 订阅
15 篇文章 3 订阅


一、PacketReader实例化


PacketReader 在 XmppConnection中被实例化:

PacketReader packetReader = new PacketReader(this);


1、PacketReader 的构造方法


PacketReader实例化执行的构造方法如下:

protected PacketReader(final XMPPConnection connection) {

        this.connection = connection;   //connect成为类的成员变量

        this.init();

    }


2、初始化


 protected void init() {

        done = false;     //只有在connectError和shutdown的时候,才有done = true; done是 parsePackets方法中条件循环的判断条件之一,只有done = false 才可以读取socket

        connectionID = null;


        readerThread = new Thread() {           //创建一个用于读取socket输入流的线程

            public void run() {

                parsePackets(this);                       //   A、数据解析和装换的入口,在readerThread线程启动前,此方法没有被执行。

            }

        };

        readerThread.setName("Smack Packet Reader (" + connection.connectionCounterValue + ")");   

        readerThread.setDaemon(true);


        // Create an executor to deliver incoming packets to listeners. We'll use a single

        // thread with an unbounded queue.

        listenerExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {     //  B、这里创建一个单线程的线程池,这个线程池是 packet 监听机制的起始点。


            public Thread newThread(Runnable runnable) {

                Thread thread = new Thread(runnable,

                        "Smack Listener Processor (" + connection.connectionCounterValue + ")");

                thread.setDaemon(true);

                return thread;

            }

        });


        resetParser();                       //此方法创建了一个解析器MXParser的实例,在下面详细分析

    }


3、解析器初始化

此方法将connect类中的封装socket 输入流的reader 作为解析器的输入,既PacketReader中的解析器MXParser输入 指向了socket 的inputStream 。

parser 为readerThread 提供了数据源。

   private void resetParser() {

        try {

            parser = new MXParser();

            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);

           parser.setInput(connection.reader);

        }

        catch (XmlPullParserException xppe) {

            xppe.printStackTrace();

        }

    }


resetParser()方法执行完后,PacketReader实例化完成,然后执行packetReader.startup()


二、启动readerThread线程


packetReader.startup()启动了readerThread线程:

    synchronized public void startup() throws XMPPException {

        readerThread.start();          //指向  parsePackets(this); 方法(即上面一、2红色的A标记处)

        try {

            int waitTime = SmackConfiguration.getPacketReplyTimeout();

            wait(3 * waitTime);

        }

        catch (InterruptedException ie) {

        }

        if (connectionID == null) {

            throw new XMPPException("Connection failed. No response from server.");

        }

        else {

            connection.connectionID = connectionID;

        }

    }


三、解析数据


parsePackets(this)是数据解析、转换和分发的入口。


    private void parsePackets(Thread thread) {

        try {

            int eventType = parser.getEventType();                     //从parser中获取数据,判断事件类型。

            do {

                if (eventType == XmlPullParser.START_TAG) {

                    int parserDepth = parser.getDepth();

                    ParsingExceptionCallback callback = connection.getParsingExceptionCallback();

                    if (parser.getName().equals("message")) {                       //处理 Message类型

                        Packet packet;

                        try {

                            packet = PacketParserUtils.parseMessage(parser);               //从parser中获取数据,生成message,完成xml到java对象的转换

                        } catch (Exception e) {

                            String content = PacketParserUtils.parseContentDepth(parser, parserDepth);

                            UnparsedMessage message = new UnparsedMessage(content, e);

                            if (callback != null) {

                                callback.messageParsingException(e, message);

                            }

                            continue;

                        }

                        processPacket(packet);                                                       //分发packet,进入packetListener 的入口。                         

                    }

                    else if (parser.getName().equals("iq")) {

                        IQ iq;

                        try {

                            iq = PacketParserUtils.parseIQ(parser, connection);               //从parser中获取数据,生成iq,完成xml到java对象的转换

                        } catch (Exception e) {

                            String content = PacketParserUtils.parseContentDepth(parser, parserDepth);

                            UnparsedIQ uniq = new UnparsedIQ(content, e);

                            if (callback != null) {

                                callback.iqParsingException(e, uniq);

                            }

                            continue;

                        }

                        processPacket(iq);                                                            //分发packet,进入packetListener 的入口。 

                    }

                    else if (parser.getName().equals("presence")) {

                        Presence presence;

                        try {

                            presence = PacketParserUtils.parsePresence(parser);               //从parser中获取数据,生成presence,完成xml到java对象的转换

                        } catch (Exception e) {

                            String content = PacketParserUtils.parseContentDepth(parser, parserDepth);

                            UnparsedPresence unpresence = new UnparsedPresence(content, e);

                            if (callback != null) {

                                callback.presenceParsingException(e, unpresence);

                            }

                            continue;

                        }

                        processPacket(presence);                                                                //分发packet,进入packetListener 的入口。

                    }

                    // We found an opening stream. Record information about it, then notify

                    // the connectionID lock so that the packet reader startup can finish.

                    else if (parser.getName().equals("stream")) {

                        // Ensure the correct jabber:client namespace is being used.

                        if ("jabber:client".equals(parser.getNamespace(null))) {

                            // Get the connection id.

                            for (int i=0; i<parser.getAttributeCount(); i++) {

                                if (parser.getAttributeName(i).equals("id")) {

                                    // Save the connectionID

                                    connectionID = parser.getAttributeValue(i);

                                    if (!"1.0".equals(parser.getAttributeValue("", "version"))) {

                                        // Notify that a stream has been opened if the

                                        // server is not XMPP 1.0 compliant otherwise make the

                                        // notification after TLS has been negotiated or if TLS

                                        // is not supported

                                        releaseConnectionIDLock();

                                    }

                                }

                                else if (parser.getAttributeName(i).equals("from")) {

                                    // Use the server name that the server says that it is.

                                    connection.config.setServiceName(parser.getAttributeValue(i));

                                }

                            }

                        }

                    }

                    else if (parser.getName().equals("error")) {

                        throw new XMPPException(PacketParserUtils.parseStreamError(parser));

                    }

                    else if (parser.getName().equals("features")) {

                        parseFeatures(parser);

                    }

                    else if (parser.getName().equals("proceed")) {

                        // Secure the connection by negotiating TLS

                        connection.proceedTLSReceived();

                        // Reset the state of the parser since a new stream element is going

                        // to be sent by the server

                        resetParser();

                    }

                    else if (parser.getName().equals("failure")) {

                        String namespace = parser.getNamespace(null);

                        if ("urn:ietf:params:xml:ns:xmpp-tls".equals(namespace)) {

                            // TLS negotiation has failed. The server will close the connection

                            throw new Exception("TLS negotiation has failed");

                        }

                        else if ("http://jabber.org/protocol/compress".equals(namespace)) {

                            // Stream compression has been denied. This is a recoverable

                            // situation. It is still possible to authenticate and

                            // use the connection but using an uncompressed connection

                            connection.streamCompressionDenied();

                        }

                        else {

                            // SASL authentication has failed. The server may close the connection

                            // depending on the number of retries

                            final Failure failure = PacketParserUtils.parseSASLFailure(parser);

                            processPacket(failure);

                            connection.getSASLAuthentication().authenticationFailed(failure.getCondition());

                        }

                    }

                    else if (parser.getName().equals("challenge")) {

                        // The server is challenging the SASL authentication made by the client

                        String challengeData = parser.nextText();

                        processPacket(new Challenge(challengeData));

                        connection.getSASLAuthentication().challengeReceived(challengeData);

                    }

                    else if (parser.getName().equals("success")) {

                        processPacket(new Success(parser.nextText()));

                        // We now need to bind a resource for the connection

                        // Open a new stream and wait for the response

                        connection.packetWriter.openStream();

                        // Reset the state of the parser since a new stream element is going

                        // to be sent by the server

                        resetParser();

                        // The SASL authentication with the server was successful. The next step

                        // will be to bind the resource

                        connection.getSASLAuthentication().authenticated();

                    }

                    else if (parser.getName().equals("compressed")) {

                        // Server confirmed that it's possible to use stream compression. Start

                        // stream compression

                        connection.startStreamCompression();

                        // Reset the state of the parser since a new stream element is going

                        // to be sent by the server

                        resetParser();

                    }

                }

                else if (eventType == XmlPullParser.END_TAG) {

                    if (parser.getName().equals("stream")) {

                        // Disconnect the connection

                        connection.disconnect();

                    }

                }

                eventType = parser.next();

            } while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);          //此处判断done 等条件,开始进入循环体

        }

        catch (Exception e) {

            // The exception can be ignored if the the connection is 'done'

            // or if the it was caused because the socket got closed

            if (!(done || connection.isSocketClosed())) {

                // Close the connection and notify connection listeners of the

                // error.

                connection.notifyConnectionError(e);

            }

        }

    }


五、处理数据

上面第四步中,所有的  processPacket() 方法都是同一个,即 


  private void processPacket(Packet packet) {

        if (packet == null) {

            return;

        }


        // Loop through all collectors and notify the appropriate ones.

        for (PacketCollector collector: connection.getPacketCollectors()) {

            collector.processPacket(packet);

        }


        // Deliver the incoming packet to listeners.

        listenerExecutor.submit(new ListenerNotification(packet));  //在这里packet 进入线程池,并由ListenerNotification开启监听模式。

    }



总结:

readerThread 线程启动后,以 parser 为数据源,从socket inputStream中读取数据并解析,完成从xml数据到java对象的转换过程。

XML数据可以分为很多种类型,目前比较关注的是 Message 、Iq、Presence 。


补充:

第三步,parsePacket()方法中,会根据 parser.getName() 得到的类型调用不同的解析方法,这些方法针对 Message,IQ和Presence是不同的。

parsePackets(Thread thread) 用到了 

PacketParserUtils 类,

PacketParserUtils有一点特殊的地方。


在解析Message、IQ 和 Presence的时候,用到了 

ProviderManager.getInstance()  方法。

在connect.connect()执行成功后,在解析 IQ、Message、Presence之前,要设置

ProviderManager.getInstance().addIQProvider("notification", "androidpn:iq:notification", new NotificationIQProvider());

或者

ProviderManager.getInstance().addExtensionProvider("myExtension""androidpn:iq:myextension"new MyExtensionProvider());



下一步 从ListenerNotification做为入口分析。


<EOF>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值