Android IM之基於Openfire+Smack的聊天伺服器的搭建與測試
XMPP協議(Extensible Messaging and PresenceProtocol,可擴充訊息處理現場協議)是一種基於XML的協議,目的是為瞭解決及時通訊標準而提出來的,最早是在Jabber上實現的。它繼承了在XML環境中靈活的發展性。因此,基於XMPP的應用具有超強的可擴充性。並且XML很易穿過防火牆,所以用XMPP構建的應用不易受到防火牆的阻礙。利用XMPP作為通用的傳輸機制,不同組織內的不同應用都可以進行有效通訊。
先來瞭解幾個概念
Openfire主要是作為伺服器,負責管理用戶端的通訊串連,以及提供用戶端一些通訊資訊和串連資訊。
Smack主要是xmpp協議的實現,提供了一套很好的api,所以下面操作xmpp都是通過使用Smack的api來實現,從4.1.0開始,它就支援Android了,所以我們直接使用Smack即可,當然在這不支援之前是使用Asmack這個包的,裡面方法跟smack包差不多。
Spark 是IM用戶端的實現,其實就是使用了Smack 的api實現的。
第二個是一套基於XMPP實現的API,我們直接引用其即可,在Android Studio中,我們直接在gradle中添加依賴即可。
compile 'org.igniterealtime.smack:smack-android-extensions:4.1.4' compile 'org.igniterealtime.smack:smack-tcp:4.1.4'
然後我們需要添加網路許可權
接下來我們先不管android端,我們先進行兩個軟體的安裝。首先安裝openfire.
點擊安裝包開啟進行初始化
選擇語言,這裡選擇中文
確定後再點下一步
同意許可點擊下一步
選擇安裝目錄點擊下一步,這裡是預設目錄
繼續下一步
耐心等待檔案解壓完成
點擊完成後運行Openfire
運行成功後點擊Launch Admin進入後台完成剩下的安裝工作
選擇語言
伺服器配置,我們將域修改為原生區域網路IP地址
我們需要獲得我們電腦的IP地址。
獲得的IP地址為10.0.0.24,將域修改為這個值
資料庫我們選擇使用外部資料庫,所以勾選第一個
接下來就是一些值,第一項的下拉選擇mysql,之後值會被填充。接下來我們就需要在mysql中添加一個資料庫。
這裡假設你的本地有mysql伺服器,開啟後台,添加一個使用者,勾選建立與使用者名稱同名的資料庫並授予所有許可權
把database name和hostname修改成對應的值,使用者名稱和密碼為你剛才mysql中建立的使用者和密碼
選擇初始設定
設定openfire管理員帳號密碼,這裡帳號設定為admin,密碼自己設定
點擊登入到管理主控台
進入到後台,輸入帳號密碼進行登陸
登陸成功後就是後台了
然後安裝Spark,點擊下載的安裝包
選擇安裝目錄
點擊下一步
繼續點擊下一步
等待安裝完成
點擊finish運行spark
使用我們的管理員帳號admin進行登陸,伺服器為本地,127.0.0.1
如果登陸成功了就會出現下面的介面
然後我們添加兩個測試帳號,在openfire後台,輸入這些資訊進行添加使用者
添加了兩個測試帳號,分別為test和test1
接下來最重要的事就是Android端了,在這之前,我們需要讓我們的手機和電腦出於同一個區域網路內,如果你使用的是模擬器,那麼,不存在這個問題。
獲得的IP地址為10.0.0.24,接下來就是編寫代碼進行登陸了。
獲得一個串連
private XMPPTCPConnection getConnection(){ String server=10.0.0.24; int port=5222; XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder(); builder.setServiceName(server); builder.setHost(server); builder.setPort(port); builder.setCompressionEnabled(false); builder.setDebuggerEnabled(true); builder.setSendPresence(true); builder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled); XMPPTCPConnection connection = new XMPPTCPConnection(builder.build()); return connection; }
初始設定變數
private EditText account, password,to,content;private Button login,logout,send;private XMPPTCPConnection connection;connection=getConnection();account = (EditText) findViewById(R.id.account);password = (EditText) findViewById(R.id.password);to = (EditText) findViewById(R.id.to);content = (EditText) findViewById(R.id.content);login = (Button) findViewById(R.id.login);logout = (Button) findViewById(R.id.logout);send = (Button) findViewById(R.id.send);login.setOnClickListener(this);logout.setOnClickListener(this);send.setOnClickListener(this);
對應的點擊事件的實現,也就是登陸,登出,發送訊息的邏輯
@Override public void onClick(View view) { switch (view.getId()){ case R.id.login:{ final String a = account.getText().toString(); final String p = password.getText().toString(); if (TextUtils.isEmpty(a) || TextUtils.isEmpty(p)) { Toast.makeText(getApplicationContext(), 帳號或密碼不可為空, Toast.LENGTH_LONG).show(); return; } new Thread(new Runnable() { @Override public void run() { try { connection.connect(); connection.login(a, p); Presence presence = new Presence(Presence.Type.available); presence.setStatus(我是線上狀態); connection.sendStanza(presence); ChatManager chatmanager = ChatManager.getInstanceFor(connection); chatmanager.addChatListener(new ChatManagerListener() { @Override public void chatCreated(Chat chat, boolean createdLocally) { chat.addMessageListener(new ChatMessageListener() { @Override public void processMessage(Chat chat, Message message) { String content=message.getBody(); if (content!=null){ Log.e(TAG, from: + message.getFrom() + to: + message.getTo() + message: + message.getBody()); android.os.Message message1= android.os.Message.obtain(); message1.what=1; message1.obj=收到訊息: + message.getBody()+ 來自:+message.getFrom(); mHandler.sendMessage(message1); } } }); } }); } catch (SmackException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (XMPPException e) { e.printStackTrace(); } } }).start(); break; } case R.id.logout: connection.disconnect(); break; case R.id.send: final String t = to.getText().toString(); final String c = content.getText().toString(); if (TextUtils.isEmpty(t)||TextUtils.isEmpty(c)) { Toast.makeText(getApplicationContext(), 接收方或內容, Toast.LENGTH_LONG).show(); return; } try { ChatManager chatmanager = ChatManager.getInstanceFor(connection); Chat mChat = chatmanager.createChat(t+@10.0.0.24); mChat.sendMessage(c); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } break; } }
收到訊息後需要在主線程裡操作,簡單的Toast一下
private Handler mHandler=new Handler(){ @Override public void handleMessage(android.os.Message msg) { switch (msg.what){ case 1: Toast.makeText(getApplicationContext(),msg.obj+,Toast.LENGTH_SHORT).show(); break; } super.handleMessage(msg); }};
這時候如果你使用測試帳號進行登陸,你會發現登陸不了,會報一個錯誤
解決方案也比較簡單,到Openfire的安裝目錄中,尋找conf/openfire.xml檔案
在最後一個節點閉合前加入代碼
PLAIN
重啟OpenFire,這時候你會發現成功登陸了,並能正常的設定使用者的線上狀態了
將我們的Spark使用測試帳號test1登陸,Android端使用test登陸,測試訊息是否能成功送到。
源碼。