Openfire XMPP Smack RTC IM 即时通讯 聊天


目录

目录
简介
Openfire 简介
相关的几个名词
Smack
Spark
JID
XMPP
Openfire 安装配置
测试代码
初始化
登录服务器
登录底层报文通讯简要解析
登录底层报文通讯简要解析
服务器判断客户端是否在线
发送消息
测试案例代码
项目结构
MainActivity
常用功能封装的工具栏

简介

Demo地址:https://github.com/baiqiantao/OpenFireTest.git

官网

官方文档

OpenFire下载

Openfire 简介

Openfire

  • Openfire是一个根据开源Apache许可证授权的实时协作服务器 real time collaboration (RTC)。它使用唯一广泛采用的即时消息开放协议XMPP(Jabber)。 Openfire非常容易设置和管理,但提供坚如磐石的安全性和性能。
  • Openfire是一个功能丰富即时消息和跨平台实时协作服务器,使用XMPP协议提供全面的群聊和即时消息服务
  • OpenFire是采用Java编程语言开发的实时协作服务器,可以轻易的构建高效率的即时通信服务器,安装和使用简单,利用 Web 进行管理,单台服务器可支持上万并发用户

相关的几个名词

简单说,OpenFire 是服务器,XMPP 是协议,Smack 是类库,Spark 是客户端。

Smack

GitHub

Flowdalic/asmack

  • Smack 是一个基于 XMPP 协议的 Java 实现,提供一套可扩展的API,与 OpenFire 进行通信。
  • Smack 是一个开源,易于使用的 XMPP 客户端类库,可以实现即时通讯和聊天。
  • Smack 是Spark项目的核心。

优点:

  • 简单,功能强大,只需短短几行代码就可以向用户发送文本消息;
  • 不像其他类库那样强制你进行包级别的编码,Smack提供了智能的、更高级的构造,像Chat和Roster类,可以让你进行更高效的编程;
  • 你不需要熟悉 XMPP XML 格式,甚至不需要熟悉XML;
  • 提供了简单的机器到机器通讯,允许在每个消息中设置任意数量的属性,包括java对象;
  • Apache许可下的开源类库,这意味着使用者可以将Smack整合进商业的或者非商业的应用中。

缺点是其API并非为大量并发用户设计,每个客户要1个线程,占用资源大。

Spark

  • Spark 相当与电脑版QQ,通过 smack 与 openfire 进行通信。
  • Spark 是一个 XMPP 协议通信聊天的CS端的IM软件,它可以通过 openfire 进行聊天对话。
<message from="[email protected]" to="[email protected]">消息内容</message>

JID

  • 基于历史原因, 一个XMPP实体的地址称为Jabber IdentifierJID,它用来标示XMPP网络中的各个XMPP实体。
  • 鉴于协议的分布式特征, JID 应包含联系到用户所需的所有信息。
  • 个人认为可以把JID理解为Email地址,就比较好理解了。
  • 一个合法的JID包括节点名user、域名domain、资源名resource,其中 user 和 resource 是可有可无的,domain 是必须的。domain和user部分是不分大小写的,但是resource区分大小写。
    • domainpart 通常指网络中的网关或者服务器
    • localpart(user、node) 通常表示一个向服务器或网关请求和使用网络服务的实体(比如一个客户端),当然它也能够表示其他的实体(比如在多用户聊天系统中的一个房间)。
    • resourcepart:通常表示一个特定的会话(与某个设备),连接(与某个地址),或者一个附属于某个节点ID实体相关实体的对象(比如多用户聊天室中的一个参加者)。
  • JID的格式为:jid = [ localpart "@" ] domainpart [ "/" resourcepart ],例如:
    • [email protected]:表示服务器jabber.org上的用户stpeter
    • [email protected]:一个用来提供多用户聊天服务的特定的聊天室。这里 room 是聊天室的名字,service 是多用户聊天服务的主机名
    • [email protected]/nick:加入了聊天室的用户nick的地址。这里 nick 是用户在聊天室的昵称

XMPP

Extensible Messaging and Presence Protocol,可扩展通讯和表示协议

  • XMPP 是基于 XML 的协议,这表明 XMPP 是可扩展的。
  • XMPP 包含了针对服务器端的软件协议,用于即时消息以及在线现场探测。
  • XMPP 的前身是Jabber(1998 年),一个开源形式组织产生的网络即时通信协议。
  • XMPP 是一个由IETF标准化的开放协议,由XMPP标准基金会支持和扩展。

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=‘[email protected]/phone‘ id=‘5U6Mk-5‘ to=‘[email protected]‘ type=‘chat‘>
    <body>{"fromId":"8049a646c63e65e8","fromName":"韩大东","messageType":1,"secret":false,"textContent":"你好","toName":"郑西风","toUserID":"903e652d2334628a"}</body>
    <request xmlns=‘urn:xmpp:receipts‘/>
</message>

客户端 8049a646c63e65e8 接收到的消息:

<message from="[email protected]/phone" id="Bw4c9-4" to="[email protected]" 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协议集而设计实现的,这些应用具有很好的互通性。

Openfire 安装配置

安装时除了修改一下安装路径,其他一路Next就Ok了。

安装完毕后会自动启动Openfire服务并自动打开 配置页面 (可能需要手动刷新一下)。也可以通过双击 \Openfire\bin\openfire.exe\Openfire\bin\openfired.exe 启动Openfire服务后手动打开配置页面。

然后按照指引设置 Openfire 服务器:

  • 选择语言:中文简体
  • 配置服务器域名【127.0.0.1】

  • 选择数据库

  • 选择特性配置,默认即可
  • 设置管理员帐户【[email protected]】【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=‘[email protected]‘
               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=‘[email protected]‘
               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=‘[email protected] 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>[email protected]/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="[email protected]/c997c3a8"/>

3、接着会自动发送一条获取通讯录的指令

<iq
    id=‘gZYnq-5‘
    type=‘get‘>
    <query xmlns=‘jabber:iq:roster‘></query>
</iq>

服务器将返回

<iq
    id="SG6jR-5"
    to="[email protected]/phone"
    type="result">
    <query ver="-491295515"
           xmlns="jabber:iq:roster">
        <item
            name="李**"
            jid="[email protected]"
            subscription="to"/>
        <item
            jid="[email protected]"
            subscription="from"/>
        <item
            ask="subscribe"
            jid="2[email protected]"
            subscription="none"/>
    </query>
</iq>

服务器判断客户端是否在线

服务器会定时(3分钟)主动发送一条 ping 消息,以确定客户端是否在线:

<iq
    from="oatest.dgcb.com.cn"
    id="553-595"
    to="[email protected]/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=‘[email protected]/phone‘
    id=‘WRULf-15‘
    to=‘[email protected]/phone‘
    type=‘chat‘>
    <body>你好,我是包青天</body>
    <thread>a86ee445-0028-4058-8d08-98803c9b6fdb</thread>
</message>

发送方式二:

XMPPUtils.getConnection().sendStanza(msg);//发送一个Message对象,可包含一些信息,一般使用后者
<message
    from=‘[email protected]/phone‘
    id=‘1539957065416‘
    to=‘[email protected]/phone‘
    type=‘chat‘>
    <body>你好,我是包青天</body>
</message>

服务器回执:

<message
    from="[email protected]/phone"
    to="[email protected]/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‘

MainActivity

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 ? "[email protected]/phone" : "[email protected]/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("[email protected]");
            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

原文地址:https://www.cnblogs.com/baiqiantao/p/9819242.html

时间: 2024-10-29 01:53:54

Openfire XMPP Smack RTC IM 即时通讯 聊天的相关文章

黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.exception.site/essay/how-to-use-gitter-on-your-website-quickly 一.前言 小哈从很早以前就有写博文的习惯,不过那个时候,也没咋认真地写,倒是挺喜欢倒腾,从最初在 CSDN 写博客,写了得有 100 多篇后,那时,CSDN 开始加入了恶心的广告,体验开始

脱离Tomcat环境下使用WebSocket即时通讯聊天的示例

A 代码生成器(开发利器);     增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成   就不用写搬砖的代码了,生成的放到项目里,可以直接运行B 阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势C 安全权限框架shiro ;  Shiro 是一个用 Java 语言实现的框架,通过一个简单易用的 API 提供身份验证和授权,更安

安卓即时通讯聊天界面及动态表情实现

最近因工作需要,一直在研究即时通讯,偶尔在网上看到一篇介绍发送类似qq动态表情的文章,觉得很不错,于是经过本人的整理及改善,做成一个demo,供大家一块研究学习,界面及功能如下: 其中接收的消息是在自己发送消息后模拟对方发来的消息已达到效果,具体可下载demo研究,里面的注释也比较清楚,有这方面需求的同学可以简单的学习下其布局,收发消息后的处理以及发送表情等的实现过程. 源码地址:http://download.csdn.net/detail/baiyuliang2013/7973133

openfire+asmack搭建的安卓即时通讯(七) 15.5.27

本地化之章! 往期传送门: 1.http://www.cnblogs.com/lfk-dsk/p/4398943.html 2.http://www.cnblogs.com/lfk-dsk/p/4411625.html 3.http://www.cnblogs.com/lfk-dsk/p/4412126.html 4.http://www.cnblogs.com/lfk-dsk/p/4413693.html 5.http://www.cnblogs.com/lfk-dsk/p/4419418.h

openfire+asmack搭建的安卓即时通讯(五) 15.4.12

这一篇博客其实是要昨天写的,但昨天做了作修改就停不下来了,这次的修改应该是前期开发的最终回了,其余的功能有空再做了,下周可能要做一些好玩的东西,敬请期待! 1.修改下Logo:(Just We) http://romannurik.github.io/AndroidAssetStudio/   可以用这个网站来做哦,上传个图片就可以哦! 2.添加欢迎页: 我自己画了个Just We的欢迎页 这里是添加欢迎页活动的代码,把程序的启动活动换为Open活动: 1 public class Open e

openfire+asmack搭建的安卓即时通讯(六) 15.4.16

啊啊啊啊啊啊啊啊,这东西越做越觉得是个深坑啊! 1.SharedPreferences.Editor的密码保存和自动登录: 首先还是从主界面开始,因为要提升一下用户体验自然要加入保存密码和自动登录的功能. 1 <CheckBox 2 android:text="保存密码" 3 android:layout_weight="1" 4 android:id="@+id/save_password" 5 android:layout_width=

openfire+asmack搭建的安卓即时通讯(二) 15.4.9

www.meimeidu.com/Theme/Details/150708/www.meimeidu.com/Theme/Details/150707/www.meimeidu.com/Theme/Details/150706/www.meimeidu.com/Theme/Details/150705/www.meimeidu.com/Theme/Details/150704/www.meimeidu.com/Theme/Details/150703/www.meimeidu.com/Theme

openfire+asmack搭建的安卓即时通讯(四) 15.4.10

之前的教程不知道你们成功了没,,,没成功可以问我啊=-= 第四篇博文是要实现发送消息的功能. 首先在我们登陆后的活动的layout里添加这样的两个控件,一个EditText和一个Button用于发送数据 1 <EditText android:id="@+id/edit_text_out" 2 android:layout_width="0dp" 3 android:layout_height="wrap_content" 4 androi

java socket 模拟im 即时通讯

自己想了一下怎么实现,就写了,没有深究是否合理.更多处理没有写下去,例如收件人不在线,应该保存在数据库,等下一次连接的时候刷新map,再把数据发送过去,图片发送也没有做,也没有用json格式 socket很奇怪,我用客户端连接上了服务器,没有发送消息的情况下,断开电脑网络,是不会出现问题,然后在把电脑网络连接上,通讯依然正常,正常断开也不出问题,但是用idea直接按stop键,那么服务端就会出问题了,读取事件会一直为true,造成死循环,消耗CPU,所以必须要判断一下客户端连接是否断开了 只需要