Openfire XMPP Smack RTC IM 即时通讯 聊天
Demo地址:https://github.com/baiqiantao/OpenFireTest.git 
官网 
官方文档 
OpenFire下载 

Openfire
实时协作服务器 real time collaboration (RTC)。它使用唯一广泛采用的即时消息开放协议XMPP(Jabber)。 Openfire非常容易设置和管理,但提供坚如磐石的安全性和性能。功能丰富的即时消息和跨平台实时协作服务器,使用XMPP协议提供全面的群聊和即时消息服务。简单说,OpenFire 是服务器,XMPP 是协议,Smack 是类库,Spark 是客户端。
基于 XMPP 协议的 Java 实现,提供一套可扩展的API,与 OpenFire 进行通信。XMPP 客户端类库,可以实现即时通讯和聊天。优点:
缺点是其API并非为大量并发用户设计,每个客户要1个线程,占用资源大。
<message from="admin@myopenfire.com" to="bqt@myopenfire.com">消息内容</message>Jabber Identifier或JID,它用来标示XMPP网络中的各个XMPP实体。联系到用户所需的所有信息。网关或者服务器实体(比如一个客户端),当然它也能够表示其他的实体(比如在多用户聊天系统中的一个房间)。jid = [ localpart "@" ] domainpart [ "/" resourcepart ],例如:
服务器jabber.org上的用户stpeter。聊天室的名字,service 是多用户聊天服务的主机名。昵称。Extensible Messaging and Presence Protocol,可扩展通讯和表示协议
XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。
优点:开放、可扩展、标准、证实可用、分散、安全 
缺点 :数据负载过重,没有二进制传输
基本网络结构 
XMPP中定义了三个角色,客户端,服务器,网关,通信能够在这三者的任意两个之间双向发生。 
服务器同时承担了客户端信息记录,连接管理和信息的路由功能。 
网关承担着与异构即时通信系统的互联互通,异构系统可以包括SMS,MSN,ICQ等。 
基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在之上传输XML。
XMPP 工作流程
XMPP核心协议通信的基本模式就是先建立一个stream,然后协商一堆安全之类的东西,中间通信过程就是客户端发送XML Stanza(节点),一个接一个的。服务器根据客户端发送的信息以及程序的逻辑,发送XML Stanza给客户端。但是这个过程并不是一问一答的,任何时候都有可能从一方发信给另外一方。通信的最后阶段是</stream>关闭流,关闭TCP/IP连接。
传输的内容 
传输的是与即时通讯相关的指令。在以前这些命令要么用2进制的形式发送(比如QQ),要么用纯文本指令加空格加参数加换行符的方式发送(比如MSN)。而XMPP传输的即时通讯指令的逻辑与以往相仿,只是协议的形式变成了XML格式的纯文本。这不但使得解析容易了,人也容易阅读了,方便了开发和查错。 
XMPP 的核心部分就是一个在网络上分片段发送 XML 的流协议。这个流协议是 XMPP 的即时通讯指令的传递基础,可以说 XMPP 用 TCP 传的是 XML 流。
真实通讯案例 
Xmpp协议是建立在xml的基础上的,所以,看起来,xmpp协议就像一个xml。
客户端 8049a646c63e65e8 发出去的消息:
<message from=‘8049a646c63e65e8@oatest.dgcb.com.cn/phone‘ id=‘5U6Mk-5‘ to=‘903e652d2334628a@oatest.dgcb.com.cn‘ type=‘chat‘>
    <body>{"fromId":"8049a646c63e65e8","fromName":"韩大东","messageType":1,"secret":false,"textContent":"你好","toName":"郑西风","toUserID":"903e652d2334628a"}</body>
    <request xmlns=‘urn:xmpp:receipts‘/>
</message>
客户端 8049a646c63e65e8 接收到的消息:
<message from="903e652d2334628a@oatest.dgcb.com.cn/phone" id="Bw4c9-4" to="8049a646c63e65e8@oatest.dgcb.com.cn" type="chat">
    <body>{"fromId":"903e652d2334628a","fromName":"郑西风","messageType":1,"secret":false,"textContent":"你好"}</body>
    <request xmlns="urn:xmpp:receipts"/>
    <send time="2018-10-19 16:08:21:999" xmlns="icitic:msg:single"/>
</message>
其实 XMPP 是一种很类似于http协议的一种数据传输协议,用户只需要明白它接收的类型,并理解它返回的类型,就可以很好的利用xmpp来进行数据通讯。
目前不少IM应用系统如Google公司的Google Talk以及Jive Messenger等开源应用,都是遵循XMPP协议集而设计实现的,这些应用具有很好的互通性。
安装时除了修改一下安装路径,其他一路Next就Ok了。 
安装完毕后会自动启动Openfire服务并自动打开 配置页面 (可能需要手动刷新一下)。也可以通过双击 \Openfire\bin\openfire.exe 或 \Openfire\bin\openfired.exe 启动Openfire服务后手动打开配置页面。 

然后按照指引设置 Openfire 服务器:
配置服务器域名【127.0.0.1】 

选择数据库 

选择特性配置,默认即可
设置管理员帐户【0909082401@163.com】【123456a】 

提示安装完成,点击登录管理员控制台页面【admin】【123456a】
进入后可以看到服务器名称等信息【127.0.0.1】 

创建用户【admin】【baiqiantao】【bqt】【test】 
 
  
安装spark客户端,这个spark仅仅是拿来测试用的。
至此代码以外的环境已经配置好了。
Demo地址:https://github.com/baiqiantao/OpenFireTest.git
XMPPConnection的连接需要通过XMPPTCPConnectionConfiguration.builder()配置你在Openfire设置的配置,代码如下:
/**
 * 初始化
 */
public static synchronized void init(CharSequence username, String password) {
      if (connection == null) {
            //初始化XMPPTCPConnection相关配置
            XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
                        .setUsernameAndPassword(username, password)//设置登录openfire的用户名和密码
                        .setServiceName("oatest.dgcb.com.cn")//设置服务器名称
                        .setHost("oatest.dgcb.com.cn")//设置主机地址
                        .setPort(25222)//设置端口号
                        .setResource("phone") //默认为Smack
                        .setDebuggerEnabled(true)//是否查看debug日志
                        //**********************************************  以下为进阶配置  *************************************************
                        .setConnectTimeout(10 * 1000)//设置连接超时的最大时间
                        .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)//设置安全模式,关闭安全模式
                        .setCompressionEnabled(false) //开启通讯压缩,开启后传输的流量将节省90%
                        .setSendPresence(false)
                        .setCustomSSLContext(getSSLContext()) //自定义的TLS登录
                        .setHostnameVerifier((hostname, session) -> true)
                        .build();
            connection = new XMPPTCPConnection(configuration);
            connection.addConnectionListener(new MyConnectionListener()); //监听connect状态
            //SASL认证
            SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
            SASLAuthentication.blacklistSASLMechanism(SASLPlainMechanism.DIGESTMD5);
            SASLAuthentication.registerSASLMechanism(new SASLPlainMechanism());
            Roster.getInstanceFor(connection).addRosterListener(new MyRosterListener());
            ChatManager.getInstanceFor(connection).addChatListener(new MyChatManagerListener()); //监听与聊天相关的事件
            MultiUserChatManager.getInstanceFor(connection).addInvitationListener(new MyInvitationListener()); //被邀请监听
      }
}通过了上面的配置后,咱们可以登录Openfire系统了,相当简单:
/**
 * 登录
 */
public static void login(CharSequence username, String password) {
      try {
            if (!XMPPUtils.getConnection().isConnected()) {
                  XMPPUtils.getConnection().connect();
            }
            if (XMPPUtils.getConnection().isConnected()) {
                  Log.i("bqt", "开始登录");
                  XMPPUtils.getConnection().login(username, password);
                  Log.i("bqt", "登录成功");
            } else {
                  Log.i("bqt", "登录失败");
            }
      } catch (SmackException e) {
            e.printStackTrace();
      } catch (IOException e) {
            e.printStackTrace();
      } catch (XMPPException e) {
            e.printStackTrace();
      }
}1、在建立了Socket后,client会向服务器发出一条xml: 

<stream:stream xmlns:stream=‘http://etherx.jabber.org/streams‘
               from=‘8049a646c63e65e8@oatest.dgcb.com.cn‘
               to=‘oatest.dgcb.com.cn‘
               version=‘1.0‘
               xmlns=‘jabber:client‘
               xml:lang=‘en‘>服务器解析到上面的指令后,会返回用于告诉client可选的SASL方式 

<?xml version=‘1.0‘ encoding=‘UTF-8‘?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams"
               from="oatest.dgcb.com.cn"
               id="36ebm4blnf"
               version="1.0"
               xmlns="jabber:client"
               xml:lang="en">
    <stream:features>
        <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls>
        <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
            <mechanism>PLAIN</mechanism>
            <mechanism>SCRAM-SHA-1</mechanism>
            <mechanism>CRAM-MD5</mechanism>
            <mechanism>DIGEST-MD5</mechanism>
        </mechanisms>
        <compression xmlns="http://jabber.org/features/compress">
            <method>zlib</method>
        </compression>
        <ver xmlns="urn:xmpp:features:rosterver"/>
        <register xmlns="http://jabber.org/features/iq-register"/>
    </stream:features>2、客户端选择PLAIN认证方式 

<auth mechanism=‘PLAIN‘
      xmlns=‘urn:ietf:params:xml:ns:xmpp-sasl‘>ADgwNDlhNjQ2YzYzZTY1ZTgAQkRFNEM3QzBGMzdENEZGRTlENDlGNDcwMTdFNUJCRjc=
</auth>服务器通过计算加密后的密码后,服务器将返回 

<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>3、当客户端收到以上命令后,将首次发起连接的id发送到服务器 

<stream:stream xmlns:stream=‘http://etherx.jabber.org/streams‘
               from=‘8049a646c63e65e8@oatest.dgcb.com.cn‘
               id=‘36ebm4blnf‘
               to=‘oatest.dgcb.com.cn‘
               version=‘1.0‘
               xmlns=‘jabber:client‘
               xml:lang=‘en‘>这时服务器会返回如下内容说明此时已经成功绑定了当前的Socket 

<?xml version=‘1.0‘ encoding=‘UTF-8‘?>
<stream:stream xmlns:stream="http://etherx.jabber.org/streams"
               from="oatest.dgcb.com.cn"
               id="36ebm4blnf"
               version="1.0"
               xmlns="jabber:client"
               xml:lang="en">
    <stream:features>
        <compression xmlns="http://jabber.org/features/compress">
            <method>zlib</method>
        </compression>
        <ver xmlns="urn:xmpp:features:rosterver"/>
        <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
        <session xmlns="urn:ietf:params:xml:ns:xmpp-session">
            <optional/>
        </session>
        <sm xmlns=‘urn:xmpp:sm:2‘/>
        <sm xmlns=‘urn:xmpp:sm:3‘/>
    </stream:features>4、压缩 
4.1、客户端在接收到如上的内容后会告诉服务器开启压缩
项目中没有使用压缩,所以下面的过程不存在,以下为参考别人的案例
<compress xmlns=‘http://jabber.org/protocol/compress‘><method>zlib</method></compress>服务器返回
<compressed xmlns=‘http://jabber.org/protocol/compress‘/>4.2、客户端收到服务器的响应命令后,重新建立一个Socket,发送指令
<stream:stream 
    xmlns=‘jabber:client‘       
    to=‘server domain‘ 
    xmlns:stream=‘http://etherx.jabber.org/streams‘ 
    version=‘1.0‘ 
    from=‘username@server domain‘  
    id=‘c997c3a8‘ 
    xml:lang=‘en‘>服务器将返回,不知道你有没有发现,这里的id还是那个id
<?xml version=‘1.0‘ encoding=‘UTF-8‘?>
    <stream:stream 
        xmlns:stream="http://etherx.jabber.org/streams" 
        xmlns="jabber:client" 
        from="im" 
        id="c997c3a8" 
        xml:lang="en" 
        version="1.0">
        <stream:features>
            <mechanisms 
            xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
                <mechanism>PLAIN</mechanism>
                <mechanism>ANONYMOUS</mechanism>
                <mechanism>JIVE-SHAREDSECRET</mechanism>
            </mechanisms>
            <bind 
                xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
                <session 
                    xmlns="urn:ietf:params:xml:ns:xmpp-session"/>
    </stream:features>实际上到这里客户端的登录已经完成了,但是还没算成功,接下来可以开始做绑定Socket的操作了
1、客户端发送绑定Socket的指令: 

<iq
    id=‘SG6jR-3‘
    type=‘set‘>
    <bind xmlns=‘urn:ietf:params:xml:ns:xmpp-bind‘>
        <resource>phone</resource>
    </bind>
</iq>服务器返回绑定了具有指定 JID 的客户端 

<iq
    id="SG6jR-3"
    to="oatest.dgcb.com.cn/36ebm4blnf"
    type="result">
    <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
        <jid>8049a646c63e65e8@oatest.dgcb.com.cn/phone</jid>
    </bind>
</iq>2、开启一个session
项目中没有开启一个session的逻辑,所以下面的过程不存在,以下为参考别人的案例
<iq id=‘b86j8-6‘ type=‘set‘><session xmlns=‘urn:ietf:params:xml:ns:xmpp-session‘/></iq>这时服务器返回
<iq 
    type="result" 
    id="b86j8-6" 
    to="c997c3a8@im/c997c3a8"/>3、接着会自动发送一条获取通讯录的指令 

<iq
    id=‘gZYnq-5‘
    type=‘get‘>
    <query xmlns=‘jabber:iq:roster‘></query>
</iq>服务器将返回 

<iq
    id="SG6jR-5"
    to="8049a646c63e65e8@oatest.dgcb.com.cn/phone"
    type="result">
    <query ver="-491295515"
           xmlns="jabber:iq:roster">
        <item
            name="李**"
            jid="0347a8a25e9074b0@oatest.dgcb.com.cn"
            subscription="to"/>
        <item
            jid="903e652d2334628a@oatest.dgcb.com.cn"
            subscription="from"/>
        <item
            ask="subscribe"
            jid="28af56d053cbbf3e@oatest.dgcb.com.cn"
            subscription="none"/>
    </query>
</iq>服务器会定时(3分钟)主动发送一条 ping 消息,以确定客户端是否在线: 

<iq
    from="oatest.dgcb.com.cn"
    id="553-595"
    to="8049a646c63e65e8@oatest.dgcb.com.cn/phone"
    type="get">
    <ping xmlns="urn:xmpp:ping"/>
</iq>客户端响应:
<iq
    id=‘553-595‘
    to=‘oatest.dgcb.com.cn‘
    type=‘result‘></iq>到此,整个登录流程已经成功了,接下来可以做一些用户信息的获取等操作。
发送方式一:
ChatManager.getInstanceFor(XMPPUtils.getConnection()).createChat(to).sendMessage(text);//直接发送一条文本<message
    from=‘8049a646c63e65e8@oatest.dgcb.com.cn/phone‘
    id=‘WRULf-15‘
    to=‘903e652d2334628a@oatest.dgcb.com.cn/phone‘
    type=‘chat‘>
    <body>你好,我是包青天</body>
    <thread>a86ee445-0028-4058-8d08-98803c9b6fdb</thread>
</message>发送方式二:
XMPPUtils.getConnection().sendStanza(msg);//发送一个Message对象,可包含一些信息,一般使用后者<message
    from=‘8049a646c63e65e8@oatest.dgcb.com.cn/phone‘
    id=‘1539957065416‘
    to=‘903e652d2334628a@oatest.dgcb.com.cn/phone‘
    type=‘chat‘>
    <body>你好,我是包青天</body>
</message>服务器回执:
<message
    from="903e652d2334628a@oatest.dgcb.com.cn/phone"
    to="8049a646c63e65e8@oatest.dgcb.com.cn/phone">
    <received msgId="WRULf-15"
              status="1"
              time="2018-10-19 21:50:23:848"
              xmlns="urn:xmpp:receipts"/>
</message>由于项目中有集成离线推送功能,而通过Demo登录时会认为没有正常登录,所以实现对方收不到消息,这个以后有空再走走流程。
推送的消息: 
 
 

implementation ‘org.igniterealtime.smack:smack-android:4.1.4‘
implementation ‘org.igniterealtime.smack:smack-tcp:4.1.4‘
implementation ‘org.igniterealtime.smack:smack-im:4.1.4‘
implementation ‘org.igniterealtime.smack:smack-extensions:4.1.4‘public class MainActivity extends ListActivity {
    private boolean switchUser = false;
    private EditText etAccount, etPassword, etChat;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {"初始化",
                "登录",
                "注销登录",
                "发消息",
                "获取好友信息",
                "创建聊天室",
                "加入聊天室",
                "邀请好友进入聊天室",
                "",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(array)));
        etAccount = new EditText(this);
        etPassword = new EditText(this);
        etChat = new EditText(this);
        etAccount.setText(switchUser ? "8049a646c63e65e8" : "903e652d2334628a");
        etPassword.setText(switchUser ? "BDE4C7C0F37D4FFE9D49F47017E5BBF7" : "40C61DE3492C41B1846281833434D997");
        etChat.setText(switchUser ? "903e652d2334628a@oatest.dgcb.com.cn/phone" : "8049a646c63e65e8@oatest.dgcb.com.cn/phone");
        getListView().addFooterView(etAccount);
        getListView().addFooterView(etPassword);
        getListView().addFooterView(etChat);//要聊天的用户的ID
    }
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        String account = etAccount.getText().toString();
        String password = etPassword.getText().toString();
        String jid = etChat.getText().toString();
        new Thread(() -> testApi(position, account, password, jid)).start();
    }
    private void testApi(int position, String account, String password, String jid) {
        switch (position) {
            case 0:
                XMPPUtils.init(account, password);//初始化
                break;
            case 1:
                XMPPUtils.login(account, password);//登录
                break;
            case 2:
                XMPPUtils.logout();//注销登录
                break;
            case 3:
                XMPPUtils.sendMessage(account + "@oatest.dgcb.com.cn/phone", jid, "你好,我是包青天");//发消息
                break;
            case 4:
                XMPPUtils.getMyFriends();//获取好友信息
                break;
            case 5:
                XMPPUtils.createMucRoom(jid, "包青天");//创建聊天室
                break;
            case 6:
                XMPPUtils.joinChatRoom(jid, account);//加入聊天室
                break;
            case 7:
                XMPPUtils.inviteToTalkRoom(jid, account, password, "快来参加第二十八届英雄大会");//邀请好友进入聊天室
                break;
            default:
                break;
        }
    }
}public class XMPPUtils {
    private static XMPPTCPConnection connection;
    /**
     * 初始化
     */
    public static synchronized void init(CharSequence username, String password) {
        if (connection == null) {
            //初始化XMPPTCPConnection相关配置
            XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
                    .setUsernameAndPassword(username, password)//设置登录openfire的用户名和密码
                    .setServiceName("oatest.dgcb.com.cn")//设置服务器名称
                    .setHost("oatest.dgcb.com.cn")//设置主机地址
                    .setPort(25222)//设置端口号
                    .setResource("phone") //默认为Smack
                    .setDebuggerEnabled(true)//是否查看debug日志
                    //**********************************************  以下为进阶配置  *************************************************
                    .setConnectTimeout(10 * 1000)//设置连接超时的最大时间
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)//设置安全模式,关闭安全模式
                    .setCompressionEnabled(false) //开启通讯压缩,开启后传输的流量将节省90%
                    .setSendPresence(false)
                    .setCustomSSLContext(getSSLContext()) //自定义的TLS登录
                    .setHostnameVerifier((hostname, session) -> true)
                    .build();
            connection = new XMPPTCPConnection(configuration);
            connection.setFromMode(XMPPConnection.FromMode.USER);
            connection.addConnectionListener(new MyConnectionListener()); //监听connect状态
            connection.addAsyncStanzaListener(new MyStanzaListener(), StanzaTypeFilter.MESSAGE);// 注册包的监听器
            //SASL认证
            SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
            SASLAuthentication.blacklistSASLMechanism(SASLPlainMechanism.DIGESTMD5);
            SASLAuthentication.registerSASLMechanism(new SASLPlainMechanism());
            Roster.getInstanceFor(connection).addRosterListener(new MyRosterListener());
            ChatManager.getInstanceFor(connection).addChatListener(new MyChatManagerListener()); //监听与聊天相关的事件
            MultiUserChatManager.getInstanceFor(connection).addInvitationListener(new MyInvitationListener()); //被邀请监听
        }
    }
    private static SSLContext getSSLContext() {
        SSLContext context = null;
        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, new TrustManager[]{new TLSUtils.AcceptAllTrustManager()}, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return context;
    }
    public static XMPPTCPConnection getConnection() {
        return connection;
    }
    /**
     * 登录
     */
    public static void login(CharSequence username, String password) {
        try {
            if (!XMPPUtils.getConnection().isConnected()) {
                XMPPUtils.getConnection().connect();
            }
            if (XMPPUtils.getConnection().isConnected()) {
                Log.i("bqt", "开始登录");
                XMPPUtils.getConnection().login(username, password);
                Log.i("bqt", "登录成功");
            } else {
                Log.i("bqt", "登录失败");
            }
        } catch (SmackException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (XMPPException e) {
            e.printStackTrace();
        }
    }
    /**
     * 注销登录
     */
    public static void logout() {
        XMPPUtils.getConnection().disconnect();
    }
    /**
     * 发消息
     */
    public static void sendMessage(String from, String to, String text) {
        try {
            ChatManager.getInstanceFor(XMPPUtils.getConnection()).createChat(to).sendMessage(text);//直接发送一条文本
            Message msg = new Message(to, Message.Type.chat);
            msg.setStanzaId(System.currentTimeMillis() + "");
            msg.setFrom(from);
            msg.setBody(text);
            XMPPUtils.getConnection().sendStanza(msg);//发送一个Message对象,可包含一些信息,一般使用后者
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取好友信息
     */
    public static void getMyFriends() {
        //并不需要访问网络,因为在登录后已经拿到用户的通讯录了,这里是直接从缓存中读取的
        Set<RosterEntry> set = Roster.getInstanceFor(XMPPUtils.getConnection()).getEntries();
        for (RosterEntry entry : set) {
            Log.i("bqt", "JID:" + entry.getUser() + ",Name:" + entry.getName());
        }
    }
    /**
     * 创建聊天室
     */
    public static void createMucRoom(String jid, String nickname) {
        try {
            MultiUserChat muc = MultiUserChatManager.getInstanceFor(XMPPUtils.getConnection()).getMultiUserChat(jid);
            muc.create(nickname);//昵称
            Form form = muc.getConfigurationForm();
            Form submitForm = form.createAnswerForm();
            for (FormField field : form.getFields()) {
                if (!FormField.Type.hidden.equals(field.getType()) && field.getVariable() != null) {
                    submitForm.setDefaultAnswer(field.getVariable());
                }
            }
            List<String> list = new ArrayList<>();
            list.add("20");
            List<String> owners = new ArrayList<>();
            owners.add("guochen@192.168.0.245");
            submitForm.setAnswer("muc#roomconfig_roomowners", owners);
            submitForm.setAnswer("muc#roomconfig_maxusers", list);
            submitForm.setAnswer("muc#roomconfig_roomname", "room01");
            submitForm.setAnswer("muc#roomconfig_persistentroom", true);
            submitForm.setAnswer("muc#roomconfig_membersonly", false);
            submitForm.setAnswer("muc#roomconfig_allowinvites", true);
            submitForm.setAnswer("muc#roomconfig_enablelogging", true);
            submitForm.setAnswer("x-muc#roomconfig_reservednick", true);
            submitForm.setAnswer("x-muc#roomconfig_canchangenick", false);
            submitForm.setAnswer("x-muc#roomconfig_registration", false);
            muc.sendConfigurationForm(submitForm);
        } catch (XMPPException.XMPPErrorException e) {
            e.printStackTrace();
        } catch (SmackException e) {
            e.printStackTrace();
        }
    }
    /**
     * 加入聊天室
     */
    public static void joinChatRoom(String jid, String nickname) {
        try {
            MultiUserChat muc = MultiUserChatManager.getInstanceFor(XMPPUtils.getConnection()).getMultiUserChat(jid);
            muc.join(nickname);
        } catch (SmackException.NoResponseException e) {
            e.printStackTrace();
        } catch (XMPPException.XMPPErrorException e) {
            e.printStackTrace();
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        }
    }
    /**
     * 邀请好友进入聊天室
     */
    public static void inviteToTalkRoom(String jid, String nickname, String user, String reason) {
        try {
            MultiUserChat muc = MultiUserChatManager.getInstanceFor(XMPPUtils.getConnection()).getMultiUserChat(jid);
            muc.addInvitationRejectionListener((invitee, rejectReason) -> Log.i("bqt", "拒绝了," + invitee + "," + rejectReason));
            muc.join(nickname);
            muc.invite(user, reason);
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        } catch (SmackException.NoResponseException e) {
            e.printStackTrace();
        } catch (XMPPException.XMPPErrorException e) {
            e.printStackTrace();
        }
    }
}2018-10-19
Openfire XMPP Smack RTC IM 即时通讯 聊天
原文:https://www.cnblogs.com/baiqiantao/p/9819242.html