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实现的。

以上三个中第一个和第三个的下载地址见http://www.igniterealtime.org/downloads/index.jsp#openfire,我们需要将他们下载下来,这里我们下载windows版

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第1张图片

第二个是一套基于XMPP实现的API,我们直接引用其即可,在Android Studio中,我们直接在gradle中添加依赖即可。

    compile 'org.igniterealtime.smack:smack-android-extensions:4.1.4'
    compile 'org.igniterealtime.smack:smack-tcp:4.1.4'

然后我们需要添加网络权限

    <uses-permission android:name="android.permission.INTERNET" />

接下来我们先不管android端,我们先进行两个软件的安装。首先安装openfire.

点击安装包打开进行初始化

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第2张图片

选择语言,这里选择中文

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第3张图片

确定后再点下一步

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第4张图片

同意许可点击下一步

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第5张图片

选择安装目录点击下一步,这里是默认目录

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第6张图片

继续下一步

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第7张图片

耐心等待文件解压完成

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第8张图片

点击完成后运行Openfire

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第9张图片

运行成功后点击Launch Admin进入后台完成剩下的安装工作

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第10张图片

选择语言

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第11张图片
服务器配置,我们将域修改为本机的局域网IP地址

我们需要获得我们电脑的IP地址。

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第12张图片

获得的IP地址为10.0.0.24,将域修改为这个值

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第13张图片

数据库我们选择使用外部数据库,所以勾选第一个

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第14张图片

接下来就是一些值,第一项的下拉选择mysql,之后值会被填充。接下来我们就需要在mysql中添加一个数据库。

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第15张图片

这里假设你的本地有mysql服务器,打开后台,添加一个用户,勾选创建与用户名同名的数据库并授予所有权限

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第16张图片

把database name和hostname修改成对应的值,用户名和密码为你刚才mysql中创建的用户和密码

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第17张图片

选择初始设置

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第18张图片

设置openfire管理员账号密码,这里账号设置为admin,密码自己设置

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第19张图片

点击登录到管理控制台

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第20张图片

进入到后台,输入账号密码进行登陆

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第21张图片

登陆成功后就是后台了

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第22张图片

然后安装Spark,点击下载的安装包

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第23张图片

选择安装目录

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第24张图片

点击下一步

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第25张图片

继续点击下一步

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第26张图片

等待安装完成

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第27张图片

点击finish运行spark

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第28张图片

使用我们的管理员账号admin进行登陆,服务器为本地,127.0.0.1

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第29张图片

如果登陆成功了就会出现下面的界面

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第30张图片

然后我们添加两个测试账号,在openfire后台,输入这些信息进行添加用户

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第31张图片

添加了两个测试账号,分别为test和test1

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第32张图片

接下来最重要的事就是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);
    }
};

这时候如果你使用测试账号进行登陆,你会发现登陆不了,会报一个错误

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第33张图片

解决方法也比较简单,到Openfire的安装目录中,寻找conf/openfire.xml文件

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第34张图片

在最后一个节点闭合前加入代码

  <sasl>
    <mechs> PLAIN </mechs>
  </sasl>

Android IM之基于Openfire+Smack的聊天服务器的搭建与测试_第35张图片

重启OpenFire,这时候你会发现成功登陆了,并能正常的设置用户的在线状态了

这里写图片描述

将我们的Spark使用测试账号test1登陆,Android端使用test登陆,测试消息是否能成功送到。

很简单的我们把整个流程走了一遍,后面如果有机会的话再继续研究下这个东西,其实它的功能是很强大的。

最后贴一下Android的源码。

http://download.csdn.net/detail/sbsujjbcy/9139479

你可能感兴趣的:(android,spark,smack,openfire,IM)