基於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這個類都包含了什么職責。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM