目录
目录
简介
Openfire 简介
相关的几个名词
Smack
Spark
JID
XMPP
Openfire 安装配置
测试代码
初始化
登录服务器
登录底层报文通讯简要解析
登录底层报文通讯简要解析
服务器判断客户端是否在线
发送消息
测试案例代码
项目结构
MainActivity
常用功能封装的工具栏
简介
Demo地址:https://github.com/baiqiantao/OpenFireTest.git
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服务并自动打开 配置页面 (可能需要手动刷新一下)。也可以通过双击 \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