XMPP openfire Smack 即時通信

從新整理下這篇文章。mysql

這篇文章的主要任務是使用AndroidStudio,經過Openfire,利用XMPP協議完成一個能夠即時通信、擁有好友系統的聊天軟件。android

1、服務器配置與相關庫sql

理論很少說,只談怎麼操做。下面先說三個工具。數據庫

一、mysql服務器(版本5.7.25)windows

首先電腦要安裝好mysql,這裏建議不要安裝最新版,由於本人以前就是安裝的8.0最新版,而後跟Openfire服務器進行對接的時候出現了很是多問題,解決後,用JDBC鏈接mysql時也出了許多問題,仍是沒法解決的。可是換成5.7.25版本後就沒出過一點問題,一切都很順利。下面附5.7.25windows的下載地址:數組

https://dev.mysql.com/downloads/file/?id=482771安全

二、Openfire服務器(版本:4.2.1)服務器

接着就是安裝Openfire服務器了,安裝好後進行配置,要選擇外部數據庫,並選擇鏈接mysql服務器。安裝請另尋教程,開啓服務器後要記住域名和端口,域名是本身設置的,端口一般是5222。tcp

三、Smack4.1.9ide

Smack是個工具庫,客戶端能夠用其來鏈接Openfire服務器並進行發送消息、接收消息等操做。

注意:不一樣版本的各類功能用法不少都是不一樣的,本人也從網上試過各類版本,不少代碼都無法用,只有這個4.1.9是比較順利的

在項目的gradle中添加依賴:

    compile 'org.igniterealtime.smack:smack-im:4.1.9'
    compile 'org.igniterealtime.smack:smack-tcp:4.1.9'
    compile 'org.igniterealtime.smack:smack-android-extensions:4.1.9'
    compile 'org.igniterealtime.smack:smack-android:4.1.9'

四、Spark

這是一個電腦端的已經實現好的客戶端,能夠鏈接Openfire服務器並擁有所有關於聊天軟件的功能。

在本項目中能夠起到輔助做用,固然,不用該軟件也能夠。

 

2、準備好上面這些後就能夠編寫代碼來進行操做了,下面是對各個功能的使用總結。

一、鏈接Openfire服務器

先設置鏈接信息,輔助對象等:

    //鏈接對象
private AbstractXMPPConnection mConnection;
private ChatManager chatManager;
    //服務器信息
    final String XMPP_DOMAIN = "win10.yangzhilong.cn";
    final String XMPP_HOST = "192.168.199.228";
    final int XMPP_PORT = 5222;

接着就能夠進行鏈接了:

public boolean connect() {
    //配置一個TCP鏈接
    XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
            .setServiceName(XMPP_DOMAIN)//設置服務器名稱,能夠到openfire服務器管理後臺查看
            .setHost(XMPP_HOST)//設置主機
            .setPort(5222)//設置端口
            .setConnectTimeout(20000)//設置鏈接超時時間
            .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)//設置是否啓用安全鏈接
            .build();
    XMPPTCPConnection connection = new XMPPTCPConnection(config);//根據配置生成一個鏈接
    this.mConnection = connection;
    mConnection.addConnectionListener(new XMPPConnectionListener());//監聽器
    Print("開始鏈接");
    try {
        connection.connect();//鏈接到服務器
        chatManager = ChatManager.getInstanceFor(mConnection);
        this.mConnection = connection;
        Print("鏈接成功");
        return true;
    } catch (SmackException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (XMPPException e) {
        e.printStackTrace();
    }
    return false;
}

public class XMPPConnectionListener implements ConnectionListener {

    @Override
    public void connected(XMPPConnection connection) {
        //已鏈接上服務器
    }

    @Override
    public void authenticated(XMPPConnection connection, boolean resumed) {
        //已認證
    }

    @Override
    public void connectionClosed() {
        //鏈接已關閉
    }

    @Override
    public void connectionClosedOnError(Exception e) {
        //關閉鏈接發生錯誤
    }

    @Override
    public void reconnectionSuccessful() {
        //重連成功
    }

    @Override
    public void reconnectingIn(int seconds) {
        //重連中
    }

    @Override
    public void reconnectionFailed(Exception e) {
        //重連失敗
    }
}

 二、用戶註冊

HashMap<String, String> attributes =new HashMap<String, String>();//附加信息
AccountManager.sensitiveOperationOverInsecureConnectionDefault(true);
try{
AccountManager.getInstance(
mConnection).createAccount(str_user_id,str_user_pw, attributes);//建立一個帳戶,username和password分別爲用戶名和密碼
} catch (SmackException.NoResponseException e) {
e.printStackTrace();
myApp.MakeToast("註冊失敗");
} catch (XMPPException.XMPPErrorException e) {
e.printStackTrace();
myApp.MakeToast("註冊失敗");
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
myApp.MakeToast("註冊失敗");
}

三、用戶登陸

public boolean login(String userName, String passWord) {
    Print("正在登陸...");
    try {
        if (!mConnection.isAuthenticated()) { // 判斷是否登陸
            mConnection.login(userName, passWord);
            Print("登陸成功!");
            return true;
        }
        Print("已被登陸,登陸失敗...");
        return false;
    } catch (XMPPException | SmackException | IOException e) {
        e.printStackTrace();
        Print("登陸出錯...");
        return false;
    }
}

四、獲取用戶的好友列表

void GetFriendFromServer() {
    Roster roster = Roster.getInstanceFor(mConnection);
  //不加下面這句的話,有時會獲取不到好友
if (!roster.isLoaded()){ try { roster.reloadAndWait(); } catch (SmackException.NotLoggedInException e) { e.printStackTrace(); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } Set entries = roster.getEntries(); for (Object object:entries) { RosterEntry entry = (RosterEntry)object;
       //entry中就是一個好友的信息了,能夠在下面進行其餘功能的代碼編寫
// entry.getType(); // entry.getName();//好友暱稱 // entry.getGroups();//好友所在的組 // entry.getJid().getDomain();//好友域名 // entry.getJid().getLocalpartOrNull();//好友名字 // entry.getUser();//(廢棄)好友完整名稱(包括域名) } }

五、獲取某個用戶的頭像

public Bitmap getUserHead(String user_id) throws XMPPException.XMPPErrorException{
  Bitmap user_head_bitmap = null; System.out.println(
"獲取用戶頭像信息: " + user_id); VCard vcard = new VCard(); try { vcard.load(mConnection, user_id + "@" + XMPP_DOMAIN); if (vcard.getAvatar() != null) { byte[] b = vcard.getAvatar(); //取出圖形爲Bitmap user_head_bitmap = BitmapFactory.decodeByteArray(b, 0, b.length); } } catch (SmackException.NoResponseException e1) { e1.printStackTrace(); }catch (SmackException.NotConnectedException e1) { e1.printStackTrace(); }
return user_head_bitmap; }

六、用戶修改頭像

//向服務器上傳頭像並保存
public void saveUserHead(String user_id, Bitmap bitmap) {
    if(bitmap == null) return ;
    VCard vcard = new VCard();
    try {
        vcard.load(mConnection, user_id + "@" + XMPP_DOMAIN); //注意要加上域名
        //bitmap轉爲byte數組
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
        byte[] b = baos.toByteArray();
        vcard.setAvatar(b);
        vcard.save(mConnection);
        System.out.println("保存用戶頭像成功");
    } catch (SmackException.NoResponseException e) {
        e.printStackTrace();
        MakeToast("頭像保存失敗");
    } catch (XMPPException.XMPPErrorException e) {
        e.printStackTrace();
        MakeToast("頭像保存失敗");
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
        MakeToast("頭像保存失敗");
    }
}

七、向某用戶發送消息

注意:這裏用到的Message對象並非android.os.Message,而是Smack中的:

import org.jivesoftware.smack.packet.Message;

因此,若在同份代碼中須要同時使用這兩種Message,要注意處理這個重名的問題,好比說定義的時候使用全名(android.os.Message)。

Chat也是Smack中的:

import org.jivesoftware.smack.chat.Chat;
public void SendMessage(String friend_id, String chat_content){
    Chat chat= chatManager.createChat(friend_id+"@"+XMPP_DOMAIN);//建立一個聊天,username爲對方用戶名
    Message msg=new Message();
    msg.setBody(chat_content);//消息主體
    try {
        chat.sendMessage(msg);//發送一個文本消息
        myApp.Print("發送消息成功");
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
        myApp.Print("發送消息失敗");
    }
}

八、接收消息

須要設置監聽器

public void ReceiveMessage(){
    if (chatManager != null){
        chatManager.addChatListener(new ChatManagerListener() {
            /**
             * @param chat
             * @param b    消息是否來自本地用戶
             */
            @Override
            public void chatCreated(Chat chat, boolean b) {
                if (!b) {
                    chat.addMessageListener(new ChatMessageListener() {
                        @Override
                        public void processMessage(Chat chat2, final Message message) {
                            System.out.println("ReceiveMessage");
                            String msg=message.getBody();
                            if (msg != null) {
                                String friend_id = from.getLocalpart().toString();
                                String message_content = msg;
                                //在此處能夠處理接收到的消息
                                //………
                                //若是在發送的時候有自定義消息體,好比咱們發送的時候添加了一個名爲「textColor」的消息體,在這裏就能夠根據名稱取出
//                             String color=message.getBody("textColor");
                            }
                        }
                    });
                }
            }
        });
    }
}

九、搜索用戶

搜索結果是可能有多個的,好比搜索「user」,可能出現「user1」、「user2」、「user11」。

public List<String> searchUsers(String userName) throws XMPPException.XMPPErrorException {
    List<String> result = new ArrayList<String>();
    UserSearchManager usm = new UserSearchManager(mConnection);
    try {
        Form searchForm = usm.getSearchForm("search."+XMPP_DOMAIN); //括號內的字符串須要注意
        Form answerForm = searchForm.createAnswerForm();
        answerForm.setAnswer("Username", true);
        answerForm.setAnswer("search", userName);
        ReportedData data = usm.getSearchResults(answerForm, "search."+XMPP_DOMAIN);
        List<ReportedData.Row> it = data.getRows();
        for(ReportedData.Row row:it){
            String temp_userid = row.getValues("Username").get(0);
            result.add(temp_userid);
        }
    } catch (SmackException.NoResponseException e) {
        e.printStackTrace();
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    }
    return result;
}

十、發送好友訂閱請求

進行好友操做時,最好先了解XMPP中的好友訂閱系統,瞭解添加和刪除好友時訂閱狀況變化的整個過程。這裏不作解釋。

//添加好友
public boolean addFriend(String friend_id){
    Roster roster = Roster.getInstanceFor(mConnection);
    try {
        //發送好友請求,設置組別
        roster.createEntry(friend_id + "@" + XMPP_DOMAIN, friend_id, new String[]{"Friends"});
        System.out.println("成功發送好友請求");
        return true;

    } catch (SmackException.NotLoggedInException e) {
        e.printStackTrace();
    } catch (SmackException.NoResponseException e) {
        e.printStackTrace();
    } catch (XMPPException.XMPPErrorException e) {
        MakeToast("沒法鏈接服務器");
        e.printStackTrace();
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    }
    return false;
}

十一、處理好友請求

首先須要設置監聽器,這樣才能接收到其餘用戶發來的好友訂閱請求:

private void ReceiveNewFriend(){
    //條件過濾器
    AndFilter filter = new AndFilter(new StanzaTypeFilter(Presence.class));
    //packet監聽器
    StanzaListener packetListener = new StanzaListener() {
        @Override
        public void processPacket(Stanza packet) throws SmackException.NotConnectedException {
            if (packet instanceof Presence) {
                Presence presence = (Presence) packet;
                String fromId = presence.getFrom();
                String from = presence.getFrom().split("@")[0];
                String str_toast = "";
                if (presence.getType().equals(Presence.Type.subscribe)) {
                        str_toast = from + "請求添加好友";
                } else if (presence.getType().equals(Presence.Type.subscribed)) {//對方贊成訂閱
                    str_toast = from + "贊成了您的好友請求";
                } else if (presence.getType().equals(Presence.Type.unsubscribe)) {//拒絕訂閱
                    str_toast = from + "拒絕了您的好友請求";
                } else if (presence.getType().equals(Presence.Type.unsubscribed)) {//取消訂閱
                    str_toast = from + "將你從好友中刪除";
                } else if (presence.getType().equals(Presence.Type.unavailable)) {//離線
                    str_toast = from + "下線了";
                } else if (presence.getType().equals(Presence.Type.available)) {//上線
                    str_toast = from + "上線了";
                }
                if(str_toast.length()>0){
                    myApp.MakeToast(str_toast);
                }
            }
        }
    };
    //添加監聽
    mConnection.addAsyncStanzaListener(packetListener, filter);
}

接收到了好友請求後,就能夠進行「贊成」或者「拒絕」操做了;

贊成的代碼以下:

//贊成好友請求
public boolean agreeNewFriend(String friend_id){
    Presence pres = new Presence(Presence.Type.subscribed);
    pres.setTo(friend_id+"@"+XMPP_DOMAIN);
    try {
        mConnection.sendStanza(pres); //贊成請求
        return true;
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    }
    return false;
}

拒絕的代碼以下:

//拒絕好友請求
public boolean refuseNewFriend(String friend_id){
    Presence pres = new Presence(Presence.Type.unsubscribed);
    pres.setTo(friend_id+"@"+XMPP_DOMAIN);
    try {
        mConnection.sendStanza(pres);
        return true;
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    }
    return false;
}

十二、刪除好友

public void removeFriend(String friend_id){
    Roster roster = Roster.getInstanceFor(mConnection);
    RosterEntry entry = roster.getEntry(friend_id+"@"+XMPP_DOMAIN);
    try {
        roster.removeEntry(entry);
    } catch (SmackException.NotLoggedInException e) {
        e.printStackTrace();
    } catch (SmackException.NoResponseException e) {
        e.printStackTrace();
    } catch (XMPPException.XMPPErrorException e) {
        e.printStackTrace();
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    }
}

關於XMPP和Smack的使用我就總結到這,都是從網上找的使用方法再解決了一個個坑總結的,在此寫這篇文章爲他人提供便利。

相關文章
相關標籤/搜索