基于XMPP协议的aSmack源码分析【1】

在研究如何实现Pushing功能期间,收集了很多关于Pushing的资料,其中有一个androidnp开源项目用的人比较多,但是由于长时间没有什么人去维护,听说bug的几率挺多的,为了以后自己的产品稳定些,所以就打算自己研究一下asmack的源码,自己做一个插件,androidnp移动端的源码中包含了一个叫做asmack的jar。

Reader和Writer

在asmack中有两个非常重要的对象PacketReader和PacketWriter,那么从类名上看Packet + (Reader/Wirter),而TCP/IP传输的数据,叫做Packet(包),asmack使用的是XMPP协议,XMPP简单讲就是使用TCP/IP协议 + XML流协议的组合。所以这个了对象的作用从字面上看应该是,写包与读包,作用为从服务端读写数据。

PacketWriter中一定含有一个Writer对象,这个Writer是一个输出流,同样的PacketReader对象中有一个Reader,而这个Reader是一个输入流,Writer和Reader对象就是一个简单的读写器,他们是从socket对象中获取出来后,经过装饰变成现在这个样子。

reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));

  

没有什么神奇的地方,主要看PacketWriter/Reader,这两个对象分别把对应的Writer和Reader引用到自己的内部进行操作,下面就先看一个PacketWriter。

 1  /**
 2      * Creates a new packet writer with the specified connection.
 3      *
 4      * @param connection the connection.
 5      */
 6     protected PacketWriter(XMPPConnection connection) {
 7         this.queue = new ArrayBlockingQueue<Packet>(500, true);
 8         this.connection = connection;
 9         init();
10     }

还有就是PacketWriter初始化的时候将XMPPConnection对象传了进来,因为在init方法中使用到了XMPPConnection对象的writer成员,我想说的是,为什么不直接传递writer成员?而是将整个对象XMPPConnection传了过来?其实这就是设计模式的好处,我们如果每次都传递的是自己的成员,那么如果后期有改动,实现一个新的XMPPConnection与PacketWriter关联,那么老的代码维护起来是很巨大的,如果这里XMPPConnection和他的同事类PacketWriter都有相对应的接口,(XMPPConnection的接口是Connection)那就更完美了,而这里用到的模式应该是中介者,不是绝对意义的中介者,由于形成中介者的条件比较高,所以实际开发中多是变形使用。PacketWriter对象在XMPPConnection中的connect方法中被初始化,它的最大作用是在其自身的内部创建了两个消息循环,其中一个用30s的heartbeats向服务器发送空白字符,保持长连接。而第二个循环则时刻从队列中主动取消息并发往服务器,而向外部提供的sendPacket方法则是向queue中添加消息,前面提到的循环机制都是在线程中工作,而消息的队列用的是ArrayBlockingQueue,这个无边界阻塞队列可以存放任何对象,这里存放的是Packet对象。

 1     public void sendPacket(Packet packet) {
 2         if (!done) {
 3             try {
 4                 queue.put(packet);
 5             }
 6             catch (InterruptedException ie) {
 7                 ie.printStackTrace();
 8                 return;
 9             }
10             synchronized (queue) {
11                 queue.notifyAll();
12             }
13         }
14     }
 1  while (!done && (writerThread == thisThread)) {
 2                 Packet packet = nextPacket();
 3                 if (packet != null) {
 4                     synchronized (writer) {
 5                         writer.write(packet.toXML());
 6                         writer.flush();
 7                         // Keep track of the last time a stanza was sent to the server
 8                         lastActive = System.currentTimeMillis();
 9                     }
10                 }
11             }

消息循环则是一个通过各种成员变量控制的while loop,第一行的nextPacket方法是向queue中获取Packet消息,并且通过weiter将包发出去,这样生产/消费的模型就搭建好了,这里需要注意的是,我删减了很多影响阅读的代码,并没有全部贴上。关于heartbeats循环其实也是一个在线程中运行的while loop,也是通过一些成员控制。wirter向服务端写了写什么?看下面的这个方法

 1 void openStream() throws IOException {
 2         StringBuilder stream = new StringBuilder();
 3         stream.append("<stream:stream");
 4         stream.append(" to=\"").append(connection.getServiceName()).append("\"");
 5         stream.append(" xmlns=\"jabber:client\"");
 6         stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
 7         stream.append(" version=\"1.0\">");
 8         writer.write(stream.toString());
 9         writer.flush();
10     }

XML,没错,这也是符合XMPP协议规范的一种表现吧,至于更多XMPP协议的好处,由于本人的经验有限,就不多做点评,希望后续会对其深入了解。

下面看一个PacketReader这个类都包含了什么职责。

posted @ 2013-01-23 15:12  One Kid Sky  阅读(7851)  评论(2编辑  收藏  举报