Openfire XMPP Smack RTC IM 即时通讯 聊天
Posted baiqiantao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Openfire XMPP Smack RTC IM 即时通讯 聊天相关的知识,希望对你有一定的参考价值。
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
- 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 Identifier
或JID
,它用来标示XMPP网络中的各个XMPP实体。 - 鉴于协议的分布式特征, JID 应包含
联系到用户
所需的所有信息。 - 个人认为可以把JID理解为Email地址,就比较好理解了。
- 一个合法的JID包括节点名user、域名domain、资源名resource,其中 user 和 resource 是可有可无的,domain 是必须的。domain和user部分是不分大小写的,但是resource区分大小写。
- domainpart 通常指网络中的
网关或者服务器
- localpart(user、node) 通常表示一个向服务器或网关请求和使用网络服务的
实体
(比如一个客户端),当然它也能够表示其他的实体
(比如在多用户聊天系统中的一个房间)。 - resourcepart:通常表示一个特定的会话(与某个设备),连接(与某个地址),或者一个附属于某个节点ID实体相关实体的对象(比如多用户聊天室中的一个参加者)。
- domainpart 通常指网络中的
- JID的格式为:
jid = [ localpart "@" ] domainpart [ "/" resourcepart ]
,例如:- [email protected]:表示
服务器jabber.org
上的用户stpeter
。 - [email protected]:一个用来提供多用户聊天服务的特定的聊天室。这里 room 是
聊天室
的名字,service 是多用户聊天服务的主机名
。 - [email protected]/nick:加入了聊天室的用户nick的地址。这里 nick 是用户在聊天室的
昵称
。
- [email protected]:表示
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服务并自动打开 配置页面 (可能需要手动刷新一下)。也可以通过双击 Openfireinopenfire.exe
或 Openfireinopenfired.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="[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
以上是关于Openfire XMPP Smack RTC IM 即时通讯 聊天的主要内容,如果未能解决你的问题,请参考以下文章
使用 xmpp + smack + openfire 在 android 中阻止用户
使用 Openfire 服务器和 XMPP(SMACK)客户端的 Android 聊天应用程序 [关闭]
Android 基于XMPP Smack openfire 开发的聊天室
Android基于XMPP Smack及Openfire学习笔记