当前位置: 首页 > 工具软件 > SMACK > 使用案例 >

Smack使用简介

诸葛乐逸
2023-12-01
1 概述
Smack是一个用于和XMPP服务器通信的类库,由此可以实现即时通讯和聊天。

1.1 主要优势
 简单易用,并且有十分强大的API,只需三行代码就可以向用户发关文本消息:
XMPPConnection connection = new XMPPConnection("jabber.org");
connection.login("mtucker", "password");
connection.createChat("jsmith@jivesoftware.com").sendMessage("Howdy!");

1)使用者不需要进行包级别的编码。
2)使用者不需熟悉XMPP XML格式。
3)提供了简单的设计以进行通讯,允许在每个消息中设置任意数量的属性,包括java对象。
4)Apache许可下的开源类库。
5)Smack的唯一必要条件是JDK 1.2 或更高版本。


2 smack主要流程

2.1 建立连接和登陆
XMPPConnection类用来建立到XMPP服务器的连接,下面是建立连接的例子:
// 建立一个到jabber.org服务器的连接。
XMPPConnection conn1 = new XMPPConnection("jabber.org");
// 通过一个特殊的端口建立一个到jabber.org服务器的连接。
XMPPConnection conn2 = new XMPPConnection("jabber.org", 5222);
// 建立一个到jabber.org服务器的SSL连接。
XMPPConnection conn3= new SSLXMPPConnection("jabber.org");
// 使用用户名和密码登陆。
XMPPConnection.login(String username, String password)


2.2 操作Roster
Roster可以用来跟踪其它用户的有效性。通过Roster类可以找到所有Roster登陆、他们所属的组以及每个登陆当前的存在状态。Roster通过XMPPConnection.getRoster()这个方法获得。

2.3 读写Packet
从客户端以XML格式发送到XMPP服务器的每个消息被称为一个“packet”。org.jivesoftware.smack.packet包中包含了一些类,这些类封装了XMPP所允许的三个不同的基本packet类型(message,presence和IQ)。
像Chat和GroupChat这样的类提供了更高类别的构造能够自动地创建和发送packet,但是我们也可以直接创建和发送packet。
// 创建一个新的presence. 传入false以指示我们已经无效了
Presence presence = new Presence(Presence.Type.UNAVAILABLE);
presence.setStatus("Gone fishing");
// 发送packet (假设已经有了一个名为"con"的XMPPConnection实例)
con.sendPacket(presence);

Smack提供两种方法读取收到的packet:PacketListener[packet监听器]和PacketCollector[packet收集器]。二者都是使用PacketFilter实例来决定哪个packet应该被处理。packet监听器用于事件样式的编程,而packet收集器有一个可以做轮询和阻塞操作的packet的结果队列。所以,当我们想对一个有可能随时到来的packet采取一些操作时,使用packet监听器;而当我们想等待一个特别的packet到来时,使用packet收集器。

3 发送消息
往复地发送消息处于即时通讯的核心地位。两个类辅助发送和接收消息:
org.jivesoftware.smack.Chat,用于在两个人之间发送消息。
org.jivesoftware.smack.GroupChat,用于加入聊天室在多个人之间发送消息。
Chat和 GroupChat类都是使用org.jivesoftware.smack.packet.Message packet类来发送消息。

3.1 Chat
一个chat在两个用户之间创建一个消息线程(通过线程ID)。下面这段代码演示了怎样和用户创建一个新的Chat并向他们发送一条文本消息:
// 假设我们已经创建了一个名为"connection"的XMPPConnection。
Chat newChat = connection.createChat("jsmith@jivesoftware.com");
newChat.sendMessage("Howdy!");

Chat.sendMessage(String)方法可以方便地创建一个Message对象,用字符串参数设置消息正文或额外的值,然后发送消息。使用的是Chat.createMessage()和Chat.sendMessage(Message)方法,如下面代码片段所示:

// 假设我们已经创建了一个名为"connection"的XMPPConnection。
Chat newChat = connection.createChat("jsmith@jivesoftware.com");
Message newMessage = newChat.createMessage();
newMessage.setBody("Howdy!");
message.setProperty("favoriteColor", "red");
newChat.sendMessage(newMessage);


Chat对象能够让我们很容易监听其它聊天参与者的回复。下面这段代码演示的功能类似鹦鹉学舌--它将回复对方输入的一切消息。

// 假设我们已经创建了一个名为"connection"的XMPPConnection。
Chat newChat = connection.createChat("jsmith@jivesoftware.com");
newMessage.setBody("Hi, I'm an annoying parrot-bot! Type something back to me.");
while (true) {
// 等待用户发送给我们的下一条消息。
Message message = newChat.nextMessage();
// 将对方发送过来的消息原样发送给他。
newChat.sendMessage(message.getBody());
}


以上这段代码使用了这个Chat.nextMessage()方法得到下一条消息,它将等待不确定何时到来的另一条消息。当然也有其它的方法用于等待特定时间段到来的新消息,或者可以添加一个监听器,它将在每次有消息到来时通知用户。

3.2 GroupChat
通过GroupChat连接到服务器上的聊天室,在发送或接收消息之前,必须用一个昵称加入聊天室。下面这段代码演示了连接到一个聊天室并发送一条消息:
// 假设我们已经创建了一个名为"connection"的XMPPConnection。
GroupChat newGroupChat = connection.createGroupChat("test@jivesoftware.com");
// 用昵称"jsmith"加入这处群。
newGroupChat.join("jsmith");
// 向聊天室中的其它人发送一条消息。
newGroupChat.sendMessage("Howdy!");

通常,在群中发送和接收消息和在Chat类中非常相似。

4 roster和presence
roster可以跟踪其它用户的有效性(存在),通过使用像“朋友”和“同事”这样的组来组织用户。其它IM系统如朋友列表,联系列表引用roster。
一个roster实例通过XMPPConnection.getRoster()方法获得,但仅当成功登陆服务器之后才可用。
4.1 名薄登陆
在roster中每个用户用一个RosterEntry表示,它包括:
1)一个XMPP地址(例如 jsmith@example.com)。
2)一个分配给用户的名字(例如 "Joe")。
3)登陆所属的roster组列表。如果roster登陆不属于任何组,它将被称为“unfiled entry”。
下面的代码片段打印roster中的所有登陆:
Roster roster = con.getRoster();
for (Iterator i=roster.getEntries(); i.hasNext(); ){
System.out.println(i.next());
}

也可能用方法获得单个登陆,未定义登陆列表,或者获得一个或所有roster组。

4.2 presence
roster中的每个登陆有presence与之关联。Roster.getPresence(String user)方法可以返回一个用户Presence的对象,如果用户不在线或我们没有预订用户的presence将会返回null。注意:一般而言,presence预订一般受用户是否在roster中的约束,但这并不适应所有情况。
一个用户可以有在线或离线两种presence。当用户在线时,他们的可能包含外延信息,如他们正在做什么,他们是否愿意被打扰等等。

4.3 监听roster和presence的变化
roster类的典型应用就是显示组的树型视图和含有当前presence值的登陆。
presence信息很可能经常变化,roster登陆也可能经常改变或被删除。为了监听roster和presence数据的变化,我们应该使用RosterListener。下面的代码片段注册了一个roster的RosterListener,它能够在标准输出中打印任何presence的变化。一个标准的客户端可以使用类似的代码用变化的信息来更新roster用户界面。
final Roster roster = con.getRoster();
roster.addRosterListener(new RosterListener() {
public void rosterModified() {
// 这个例子中忽略这个事件。
}
public void presenceChanged(String user) {
// 如果presence无效,将会打印"null",
System.out.println("Presence changed: " + roster.getPresence(user));
}
});


4.4 向roster中添加登陆
roster和presence使用一种基于许可的模式,用户只有在被许可的情况下才能被添加到别人的roster中。这样可以保护用户的隐私因为只有经核准的其它用户才能查看他们的presence信息。因此,只有当其它用户接受我们的请求时才能添加新的roster登陆。Smack通过以下三种方式中的一种处理presence预订请求:
1)自动接受所有presence预订请求。
2)自动拒绝所有presence预订请求。
3)手动处理presence预订请求。
通过Roster.setSubscriptionMode(int subscriptionMode)方法设置对请求的处理方式。简易客户端通常使用一种自动方式处理预订请求,而复杂客户端应该手动处理方式,请最终用户接受或拒绝请求。如果使用手动方式,应该注册一个PacketListener以监听Presence.Type.SUBSCRIBE类型的Presence packet。

5 接收消息
Smack提供灵活的框架来通过两种构造处理收到的packet:
1)org.jivesoftware.smack.PacketCollector —— 一同步等待新packet的类。
2)org.jivesoftware.smack.PacketListener —— 异步通知的packet的接口。
packet监听器用于事件样式的编程,而packet收集器有一个可以做轮询和阻塞操作的packet的结果队列。所以,当我们想对一个有可能随时到来的packet采取一些操作时,使用packet监听器;而当我们等待一个特别的packet到来时,使用packet收集器。
3)org.jivesoftware.smack.filter.PacketFilter 接口决定哪个特别的将会被传递到PacketCollector或PacketListener。 org.jivesoftware.smack.filter package包中有许多预定义的过滤器。

下面的代码片段演示注册了一个packet收集器和一个packet 监听器:
// 创建一个packet过滤器来监听来自一个特定用户的新的消息
//我们可以使用一个AndFilter来结合其它两个过滤器。
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class),
new FromContainsFilter("mary@jivesoftware.com"));
// 假设我们已经创建了一个名为"connection"的XMPPConnection。
// 首先,用我们创建的过滤器注册一个packet收集器。
PacketCollector myCollector = connection.createPacketCollector(filter);
// 接下来,创建一个packet监听器。我们可以简便地使用匿名内部类。
PacketListener myListener = new PacketListener() {
public void processPacket(Packet packet) {
// 在这里用收到的packet做些什么。
}
};
// 注册这个监听器。
connection.addPacketListener(myListener, filter);


5.1 标准Packet过滤器
Smack包括丰富的packet 过滤器集,当然我们可以通过实现PacketFilter接口创建自己的过滤器。默认的过滤器集包括:
 PacketTypeFilter ——特定类的packet过滤器。
 PacketIDFilter ——含有特定packet ID的packet过滤器。
 ThreadFilter ——含有特定线程ID的消息packet过滤器。
 ToContainsFilter ——发送到特定地址的packet过滤器。
 FromContainsFilter ——来自特定地址的packet过滤器。
 PacketExtensionFilter ——含有特定packet扩充的packet过滤器。AndFilter ——实现两个过滤器的逻辑“与”操作。
 OrFilter —— 实现两个过滤器的逻辑“或”操作。
 NotFilter ——实现一个过滤器的逻辑“非”操作。

6 Packet属性
Smack提供一个有效的机制,可以向packet附加任意属性。每个属性有一个String名字,这是一个java简单类型值(int, long, float, double, boolean)或者任何序列化对象(java对象可序列化当它实现了Serializable接口)。
6.1 使用API
所有主要对象支持属性,如Message对象。下面的代码显示如何设置属性:
Message message = chat.createMessage();
// 添加一个Color对象作为属性。
message.setProperty("favoriteColor", new Color(0, 0, 255));
// 添加一个int作为属性。
message.setProperty("favoriteNumber", 4);
chat.sendMessage(message);
使用如下代码获得这些属性:
Message message = chat.nextMessage();
// 获得一个Color对象属性。
Color favoriteColor = (Color)message.getProperty("favoriteColor");
// 获得一个intg属性,注意属性作为对象返回,我们必须把值转换为Integer,然后转换为int。
int favoriteNumber = ((Integer)message.getProperty("favoriteNumber")).intValue();


6.2 对象作为属性
使用对象作为属性值是一个非常强大和容易的交换数据的方式。
• Packet extension有更多标准方法添加额外数据到XMPP节。在某些情况下使用属性可能更方便,由于Smack自身会做处理XML的工作。
• 当你将Java对象作为属性发送时,只有运行着Java的客户端能够解释数据。所以,应该考虑将数据转换为一系列简单类型的值来代替Java对象。
• 作为属性值发送的对象必须实现Serialiable。另外,发送端和接收端都必须有同种的类,否则当系列化对象时会发生序列化异常。
• 序列化的对象可能会很大,这将使用更多的带宽和服务器资源。

6.3 XML格式
当前用于发送属性数据XML格式还不规范,所以很可能难以被不使用Smack的客户端识别。XML犹如下面所示:
<!--某块中的所有属性。 --> 
<properties xmlns="http://www.jivesoftware.com/xmlns/xmpp/properties">
<!-- 首选,一个名为"prop1"的integer型值。-->
<property>
<name>prop1</name>
<value type="integer">123</value>
<property>
<!-- 其次,一个序列化的Java对象,然后从二进制数据转换到base-64编码的文本。 -->
<property>
<name>blah2</name>
<value type="java-object">adf612fna9nab</value>
<property>
</properties>

当前支持的类型有:integer,long,float,double,boolean,string和java对象。

7 调试Smack
Smack包含两个内置的调试控制台,它可以跟踪客户端和服务器的XML通信量。smack.jar中的轻量型的调试器和smackx-debug.jar中的增强型的调试器。
调试模式可以通过两种方式激活:
1. 添加如下代码在创建新的连接之前:
XMPPConnection.DEBUG_ENABLED = true;
2. 设置Java系统属性smack.debugEnabled为true。这个系统属性可以在命令行设置:
java -Dsmack.debugEnabled=trueSomeApp
 类似资料: