Android學習筆記--基於XMPP的即時通訊
一、常見即時通訊實現
socket
openfire+asmack
環信
信鴿
融雲
二、XMPP
優勢
1. 開放性
XMPP
協議是自由、開放、公開的,並且易於瞭解。而且在客戶端、伺服器、元件、原始碼庫等方面,都已經各自有多種實現。
2.跨平臺
客戶端只要基於XMPP
協議,不管是什麼平臺(包括不同的移動終端)都可以互聯互通。
三、XMPP
協議簡介
The Extensible Messaging and Presence Protocol (可擴充套件通訊和表示協議) XMPP
以 Jabber
協議為基礎,而 Jabber
是即時通訊中常用的開放式協議。
四、資料格式
XML
是XMPP
系統架構的核心。它能表述幾乎任何一種結構化資料。特別是XMPP
XML
資料流進行客戶端一伺服器端、伺服器端一伺服器端的通訊。XML
資料流一般是由客戶端發起至服務端,XML
資料流的有效時間直接與使用者的線上會話有效時間相關聯。
XMPP
的特點是將複雜性從客戶端轉移到伺服器端。這使得客戶端編寫變得非常容易,更新系統功能也同樣變得容易。
XMPP
中定義了三個角色:XMPP
客戶端、XMPP
伺服器、閘道器。
客戶端:通過 TCP
套接字與XMPP
伺服器進行通訊
伺服器:同時承擔了客戶端資訊記錄、連線管理和資訊的路由功能
閘道器:承擔著與異構即時通訊系統的互聯互通(異構系統可以包括SMS(簡訊),MSN,ICQ等)
五、XMPP協議的地址格式(標誌)
每個客戶端需要擁有一個地址標識用於定位,XMPP
[ node "@" ] domain [ "/" resource ]
例如:
[email protected]/spark
格式與 Email
地址格式類似,但增添了 resource
項(非必需的)。上述例子可以解釋為:在 gmail.com
伺服器註冊的 charley使用者,且使用 spark
客戶端軟體登入。資源(resource
)只用來識別屬於使用者的位置或裝置等,一個使用者可以同時以多種資源與同一個XMPP
伺服器連線(說白了就是用於支援同一賬號的多客戶端登入)。
六、協議訊息格式
XMPP
協議包括3個頂層XML元素:Message、Presence和IQ。
Message
用來表示傳輸的訊息,當用戶傳送一條訊息時。就會在流的上下文中插入一個Message
元素,中間有使用者傳送的相關資訊;
Presence
用來表示使用者的狀態。當用戶改變自己的狀態時。就會在資料流的上下文中插入一個Presence元素,用來表示使用者現在的狀態;
IQ
用來表示一種請求,響應機制,從一個實體傳送請求,另外一個實體接受請求並響應。
XMPP
特點
1.客戶端通過TCP/IP
協議連線到伺服器,然後通過XML
傳輸資料。
2.XMPP
的核心部分就是一個在網路上分片斷髮送XML
的流協議。這個流協議是XMPP
的即時通訊指令的傳遞基礎,也是一個非常重要的可以被進一步利用的網路基礎協議。所以可以說,XMPP
用TCP
傳的是XML
流。
理論一大堆。。。。接下來貼程式碼
XmppManager.java
package com.example.xmppdemo.fengzhuang;
import android.util.Log;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.Form;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.ReportedData;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.packet.VCard;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.search.UserSearch;
import org.jivesoftware.smackx.search.UserSearchManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Created by Kelvin on 2016/12/12.
*/
public class XmppManager {
private static XmppManager xmppManager; //XmppManager的例項
private XmppManager(){} //私有化構造器
public static XmppManager getInstance(){
if (xmppManager == null){
synchronized (XmppManager.class){
if (xmppManager == null){
xmppManager = new XmppManager();
}
}
}
return xmppManager;
}
//XmppConnection 連線物件
private XMPPConnection xmppConnection;
//將其翻譯成中文為"花名冊",用來表示一個使用者的所有好友清單以及申請加好友的使用者清單,
// 為了便於管理,Roster中的使用者分組進行管理。
private Roster roster;
//用於接收訊息的介面
private XmppManagerCallback xmppManagerCallback;
//Debug標籤
private final String TAG="XmppManager";
/**
* 開啟網路連線
*/
private void openConnection(){
//連線物件為空或者還沒有認證的時候(isAuthenticated()方法返回值是boolean型別,意思是是否認證)
if (xmppConnection == null || !xmppConnection.isAuthenticated()){
try {
//配置連線,(引數一:伺服器ip地址,引數二:埠號,引數三:伺服器名字)
ConnectionConfiguration configuration = new ConnectionConfiguration(Constant.SERVER_HOST,
Constant.SERVER_PORT,Constant.SERVER_NAME);
//Xmpp是否可以自動重連(客戶端掉線時是否可以重新連線)
configuration.setReconnectionAllowed(true);
//設定安全模式
configuration.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
//特別補充,在設定configuaration的時候對認證的設定,程式碼如下:
//這個屬性預設值是true,設定時得需要與伺服器那邊統一,如果不一致,就算使用者註冊成功後,
// 登入時也會返回 server-unavailable(503)錯誤,我們用的是ejabberd伺服器,預設設定SASL認證開啟,
// 所以開始我設定為false,怎麼都無法登入,最後註釋這句程式碼,成功登入:)
//相當於一個許可權
configuration.setSASLAuthenticationEnabled(false);
// 狀態設為離線,為了取離線訊息
configuration.setSendPresence(true);
// 配置各種Provider,如果不配置,則會無法解析資料
configureConnection(ProviderManager.getInstance());
xmppConnection = new XMPPConnection(configuration);
//開啟連線
xmppConnection.connect();
} catch (XMPPException e) {
e.printStackTrace();
}
}
}
/**
* 配置連線
* @param pm
*/
public void configureConnection(ProviderManager pm) {
// Private Data Storage
pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
// Time
try {
pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
} catch (ClassNotFoundException e) {
Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Time");
}
// Roster Exchange
pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
// Message Events
pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
// Chat State
pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
// XHTML
pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
// Group Chat Invitations
pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider());
// Service Discovery # Items
pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
// Service Discovery # Info
pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
// Data Forms
pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
// MUC User
pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
// MUC Admin
pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
// MUC Owner
pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
// Delayed Delivery
pm.addExtensionProvider("x", "jabber:x:delay", new DelayInformationProvider());
// Version
try {
pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
} catch (ClassNotFoundException e) {
// Not sure what's happening here.
}
// VCard
pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
// Offline Message Requests
pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
// Offline Message Indicator
pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
// Last Activity
pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
// User Search
pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
// SharedGroupsInfo
pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup",
new SharedGroupsInfo.Provider());
// JEP-33: Extended Stanza Addressing
pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider());
// FileTransfer
pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
// Privacy
pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands",
new AdHocCommandDataProvider.MalformedActionError());
pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands",
new AdHocCommandDataProvider.BadLocaleError());
pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands",
new AdHocCommandDataProvider.BadPayloadError());
pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands",
new AdHocCommandDataProvider.BadSessionIDError());
pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands",
new AdHocCommandDataProvider.SessionExpiredError());
}
/**
* 獲取連結
* @return
*/
public XMPPConnection getConnection(){
if (xmppConnection == null){
openConnection();
}
return xmppConnection;
}
/**
* 關閉連結
*/
public void colseConnection(){
if (xmppConnection != null && xmppConnection.isConnected()){
xmppConnection.disconnect();
xmppConnection = null;
}
}
/**
* 登陸的方法
* @param account 賬號
* @param psw 密碼
* @return
*/
public boolean login(String account,String psw){
//判斷連線是否存在
if (getConnection() == null){
return false;
}
if (!getConnection().isAuthenticated() && getConnection().isConnected()){
try {
//登陸
getConnection().login(account,psw);
//登陸之後更改使用者狀態
Presence presence = new Presence(Presence.Type.available);
//設定使用者線上
presence.setMode(Presence.Mode.available);
//向伺服器傳送狀態
getConnection().sendPacket(presence);
//接收訊息監聽
ChatManager chatManager = getConnection().getChatManager();
chatManager.addChatListener(chatManagerListener);
return true;
} catch (XMPPException e) {
e.printStackTrace();
return false;
}
}
return false;
}
/**
* 聊天管理監聽器
*/
private ChatManagerListener chatManagerListener = new ChatManagerListener(){
@Override
public void chatCreated(Chat chat, boolean b) {
chat.addMessageListener(new MessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
//當訊息內容為空時,直接反悔
if (TVUtil.isEmpty(message.getBody())){
return;
}
//當訊息內容不可空時,通過介面回撥的把訊息內容傳出去
if (xmppManagerCallback != null){
xmppManagerCallback.receiveMsg(message);
}
}
});
}
};
/**
* 註冊使用者
* 表示的是Info/Query(資訊與查詢),它為XMPP通訊提供請求與響應機制。它與HTTP
* 協議的基本工作原理非常相似,允許獲取和設定查詢,與HTTP 的GET 和POST 動作類似。
* @return
*/
public IQ registered(String account, String psw){
if (getConnection() == null){
return null;
}
//設定註冊所需要的資訊
Registration registration = new Registration();
reg