Smack是一个用于和XMPP服务器通信的类库,由此可以实现即时通讯和聊天。
非常简单易用,并且有十分强大的 API。只需三行代码就可以向用户发送文本消息:
XMPPConnection connection = new XMPPConnection("jabber.org");
connection.login("mtucker", "password");
connection.createChat("[email protected]").sendMessage("Howdy!");
不像其它类库那样强制您进行包级别的编码。Smack提供智能的更高级的构造,像 Chat和GroupChat类,让您进行高效的编程。不需用您熟悉XMPP XML格式,即使您熟悉 XML.
提供简单的设计以进行通讯,Smack允许您在每个消息中设置任意数量的属性,包括java对象。
Apache许可下的开源类库,这意味着您可以将Smack整合进您的商业或非商业的应用中。
XMPP (eXtensible Messaging and Presence Protocol)是一个由Jabber Software Foundation (http://www.jabber.org)领导的开放的基于XML的协议,并且获得了 IETF的批准。
在使用该文档时假设您已经了解XMPP即时通讯的主要特征。在您阅读该文档的同时强烈建议您打开 Javadoc API 帮助文档作为参考。 这个文档将会向您介绍Smack API,并且简要介绍一些重要的类和概念。
Smack的唯一必要条件是JDK 1.2 或更高版本1。smack.jar文件已包含一个XML解析器,不需要其它第三方类库。
1 JDK 1.2 and 1.3 用户如果想使用SSL连接则classpath在中必须存在JSSE类库。
XMPPConnection类用来建立到XMPP服务器的连接。要建立SSL连接,要使用SSLXMPPConnection类。下面是建立连接的例子:
// 建立一个到jabber.org服务器的连接。
XMPPConnection conn1 = new XMPPConnection("jabber.org");
// 通过一个特殊的端口建立一个到jabber.org服务器的连接。
XMPPConnection conn2 = new XMPPConnection("jabber.org", 5222);
// 建立一个到jabber.org服务器的SSL连接。
XMPPConnection connection = new SSLXMPPConnection("jabber.org");
一旦您建立了一个连接,您必须通过方法XMPPConnection.login(String username, String password)使用用户名和密码登陆。
如果登陆成功,您可以通过创建新的Chat或GroupChat对象和其它用户聊天。
Roster能够让您跟踪其它用户的有效性(存在)。您可以通过使用像“朋友”和“同事”这样的组来组织用户,这样您可以发现每个用户是否在线。
使用XMPPConnection.getRoster()这个方法得到Roster。通过Roster类您可以找到所有Roster登陆、他们所属的组以及每个登陆当前的存在状态。
从客户端以XML格式发送到XMPP服务器的每个消息被称为一个“packet”。org.jivesoftware.smack.packet包中包含了一些类,这些类封装了XMPP所允许的三个不同的基本packet类型(message, presence, 和 IQ)。 像Chat和GroupChat这样的类提供了更高类别的构造能够自动地创建和发送packet,但是您也可以直接创建和发送packet。 下面是一个通过改变您的presence来让别人知道您已无效,已经"out fishing"了:
// 创建一个新的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收集器。您可以使用XMPPConnection实例创建packet收集器和监听器。
Smack是一个为使用XMPP服务器聊天和发送即时消息交流而提供的库。
Smack的主要优势:
l 使用简单且拥有强大的API。向用户发送一条文本消息只需用一下三行代码即可完成
XMPPConnection connection = new XMPPConnection("jabber.org");
connection.login("mtucker", "password");
connection.createChat("[email protected]").sendMessage("Howdy");
l 不像其它库那样,强制你在信息报级(packet level)编码。Smack提供智能的、更高级别的结构,例如:Chat和GroupChat类,这写能让你的程序效率更高。
l 你不需要熟悉XMPP XML格式,甚至不熟XML。
l 提供简单的机器到机器的通讯。Smack允许你对每一条消息设置任意数量的属性,包括Java对象属性。
l Apache许可的开放源码,你可将其用于商业的和非商业的应用。
关于XMPP
XMPP (eXtensible Messaging and Presence Protocol)是一个开放的,
如何使用本文档
本文档假定你已经熟悉XMPP即时消息的主要特征。我们推荐你在阅读该文档时打开Javadoc API作为参考。
本文档将向你介绍Smack API,并大概介绍几个重要的类和概念。
必备的条件
你只需要有JDK 1.2或之后的版本1和已经内嵌在smack..jar文件中的XML分析器,不需要第三部分库。
Ø JDK 1.2 and 1.3的用户若想使用SSL连接必须在他的类路径下有JSSE库。
XMPPConnection类是为XMPP服务器建立连接的类。若要创建SSL连接,需使用SSLXMPPConnection类,以下是创建连接的例子。
// Create a connection to the jabber.org server.
XMPPConnection conn1 = new XMPPConnection("jabber.org");
// Create a connection to the jabber.org server on a specific port.
XMPPConnection conn2 = new XMPPConnection("jabber.org", 5222);
// Create an SSL connection to jabber.org.
XMPPConnection connection = new SSLXMPPConnection("jabber.org");
如果创建了一个连接,你应该使用XMPPConnection.login(String username, String password)方法(参数为用户名和密码)进行登陆。一旦登陆成功,你就可以通过创建一个新的Chat 或GroupChat对象与其它用户聊天。
花名册让你很清楚的知道其它可用的用户。用户可以被分成像“朋友”、“合作者”这样的组,从而知道其它的用户在线还是离线。
可以使用XMPPConnection.getRoster()方法检索花名册。你可以用花名册(roster)类查找花名册的所有条目,它们所属的组以及每个条目当前呈现的状态。
从客户端发送到XMPP的每一条消息称为一个信息包,并作为XML发送。org.jivesoftware.smack.packet包含封装了三个XMPP允许的、不同的基本包类型(message, presence, and IQ)的类。像Chat和GroupChat这样的类提供更高级别的结构,它可以自动的创建和发送信息包,当然你也可以直接创建和发送信息包。以下代码是一个将你的当前状态改为“隐身“,从而不被别人看到的例子:
// Create a new presence. Pass in false to indicate we’re unavailable.
Presence presence = new Presence(Presence.Type.UNAVAILABLE);
presence.setStatus("Gone fishing");
// Send the packet (assume we have a XMPPConnection instance called "con").
con.sendPacket(presence);
Smack提供以下两种方法阅读收到的信息包:PacketListener和PacketCollector。它们都使用PacketFilter的实例来决定应该处理哪个信息包。信息包监听器(PacketListener)用于事件类型的设计,而信息包收集器(PacketCollector)有一个信息包的结果队列,你可以对其实施polling和blocking操作。所以,信息包监听器在你收到任何一个信息包,且你想对其进行操作时是有用的,而信息包收集器在你想等待某个特殊的信息包时是有用的。信息包收集器和监听器可以通过XMPPConnection的实例来创建。
Messaging using Chat and GroupChat
互相发送消息是即时通讯的核心,以下是两个在收发消息是用的类:
1. org.jivesoftware.smack.Chat – 用于两个人之间发送消息
2. org.jivesoftware.smack.GroupChat –用于加入聊天室,很多人之间相互发送消息。
Chat和GroupChat类都用org.jivesoftware.smack.packet .Message信息包类发送消息。在某些情况下,也许你希望绕过更高级别的Chat和GroupChat类直接发送和接收消息。
聊天时在两个用户间创建了一个新的线程(使用一个线程ID)。以下程序片示例了如何与一个用户进行开始聊天并发送一段文本消息:
// Assume we’ve created an XMPPConnection name "connection".
Chat newChat = connection.createChat("[email protected]");
newChat.sendMessage("Howdy!");
Chat.sendMessage(String)方法可以很方便的创建一个消息对象,方法体使用字符串类型的参数,然后发送消息。如果想在发送消息前对消息设置额外信息,可以使用Chat.createMessage() + Chat.sendMessage(Message)方法,如下例所示:
// Assume we’ve created an XMPPConnection name "connection".
Chat newChat = connection.createChat("[email protected]");
Message newMessage = newChat.createMessage();
newMessage.setBody("Howdy!");
message.setProperty("favoriteColor", "red");
newChat.sendMessage(newMessage);
使用Chat对象可以轻松的收听其它聊天者的回复。以下程序片是parrot-bot,它映射会其它用户类型的所有事情:
// Assume we’ve created an XMPPConnection name "connection".
Chat newChat = connection.createChat("[email protected]");
newMessage.setBody("Hi, I’m an annoying parrot-bot! Type something back to me.");
while (true) {
// Wait for the next message the user types to us.
Message message = newChat.nextMessage();
// Send back the same text the other user sent us.
newChat.sendMessage(message.getBody());
}
以上代码使用Chat.nextMessage()方法获得下一条消息,它必需一直等待直到收到下一条消息。也有其它的方法可以等待特定的时间来接受下一条消息(带超时接收),或者你也可以增加一个监听器,它可以在每次收到消息时通知你。
群聊在通过一个服务器连接到聊天室,你可以向一组人发送消息或接收他们的消息。在你能接收和发送消息前,你必须使用一个昵称登陆到聊天室。以下程序段可以连接到一个聊天室并发送消息:
// Assume we’ve created an XMPPConnection name "connection".
GroupChat newGroupChat = connection.createGroupChat("[email protected]");
// Join the group chat using the nickname "jsmith".
newGroupChat.join("jsmith");
// Send a message to all the other people in the chat room.
newGroupChat.sendMessage("Howdy!");
群聊时收发消息和私聊时工作原理大体一致。同样,也有方法可以获得聊天室里其它用户的列表。
花名册让你很清楚的知道其它可用的用户。用户可以被分成像“朋友”、“合作者”这样的组。其它的即时通讯系统将花名册作为好友列表、联系列表等。
当你成功登陆服务器后,可以使用XMPPConnection.getRoster()获得Roster类的实例。
花名册里的每一个用户都以一条花名册条目Item的形式呈现,包括以下几部分:
Ø 一个XMPP地址(例如:[email protected]).
Ø 分配给你的用户名 (例如: "Joe").
Ø 该条目在花名册中所在组的列表。如果该条目不属于任何一个组,将被称为“尚未分类”的条目。
以下程序段可以打印出花名册中的所有条目(组):
Roster roster = con.getRoster();
for (Iterator i=roster.getEntries(); i.hasNext(); ) {
System.out.println(i.next());
}
也有获得个人条目、尚未分类条目的列表、一个或者所有条目的方法。
花名册中的每一个条目都有相关的呈现方式。Roster.getPresence(String user)方法将通过用户的状态或当用户不在线或不同意将其在线状态显示出来时返回一个null Presence对象。
注意:一般情况下,用户是否同意显示其状态依赖于用户所在的花名册,但这不是在所有情况下都成立的。
用户也有一个在线或离线的状态,如果用户在线,他们的显示信息中将会有一些扩展的信息,例如他当前正在做什么,是否希望被打扰等等,详细内容可以参看Presence类。
Roster类的典型用途是用树状形式显示组和每一个条目(Friends)以及它的当前状态。如下图所示是 Exodus XMPP客户端的花名册。
显示的信息很可能会经常改变,也有可能是花名册的条目被改变甚至被删除。为了监视花名册的改变和显示的信息,应该使用一个花名册监听器(RosterListener)。以下代码使用Roster(它可以打印出花名册中的任何变化)注册了一个RosterListener。标准的客户端应该使用相似的代码更新花名册的用户信息(roster UI)以正确显示变化的信息。
final Roster roster = con.getRoster();
roster.addRosterListener(new RosterListener() {
public void rosterModified() {
// Ignore event for this example.
}
public void presenceChanged(String user) {
// If the presence is unavailable then "null" will be printed,
// which is fine for this example.
System.out.println("Presence changed: " + roster.getPresence(user));
}
});
花名册和显示使用基于许可的模型,这要求用户在加入别人的花名册前必须得到允许。这样,确保只有被允许的人才可以看到自己所显示的信息,从而保护了用户的隐私。因此,在你想添加一个新的条目,且对方没有接受你的请求前,该条目将处于等待状态。
如果另一个用户请求同意显示,从而你他们可以将你加入他们的花名册,你必须接受或拒绝请求。Smack通过以下三种方式之一操作同意显示请求:
Ø 自动接受所有的同意显示请求。
Ø 自动拒绝所有的同意显示请求。
Ø 手动处理同意显示请求。
可以使用Roster.setSubscriptionMode(int subscriptionMode)方法设置模式。简单的客户通常使用一个自动接受或拒绝同意显示请求的模式,而用更多特征的用户应该使用手动处理同意显示请求的模式,并让终端用户接受或拒绝每一个请求。如果使用手动模式,应该声明一个信息包监听器(PacketListener)来监听有Presence.Type.SUBSCRIBE类型的显示信息包。
Smack提供一个使用以下两个结构的灵活框架来处理收到的信息包:
l org.jivesoftware.smack.PacketCollector – 一个允许你同步地等待新的信息包的类
l org.jivesoftware.smack.PacketListener – 一个异步地通知你收到信息包的接口
信息包监听器(packet listener)用于事件类型的设计,而信息包收集器(packet collector)有一个信息包的结果队列,你可以对其实施polling和blocking操作。所以,信息包监听器在你收到任何一个信息包,且你想对其进行操作时是有用的,而信息包收集器在你想等待某个特殊的信息包时是有用的。信息包收集器和监听器可以通过XMPPConnection的实例来创建。
由org.jivesoftware.smack.filter.PacketFilter接口来决定哪个特殊的信息包将被转交给信息包收集器(PacketCollector)或信息包监听器(PacketListener)。可以在org.jivesoftware.smack.filter包中找到许多预先定义的过滤器。
以下代码阐释了如何注册一个信息包收集器(packet collector)和信息包监听器(packet listener):
// Create a packet filter to listen for new messages from a particular user.
// We use an AndFilter to combine two other filters. 代表接收什么样的包
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class),
new FromContainsFilter("[email protected]"));
// Assume we’ve created an XMPPConnection name "connection".
// First, register a packet collector using the filter we created.
PacketCollector myCollector = connection.createPacketCollector(filter);
// Normally, you’d do something with the collector, like wait for new packets.
// Next, create a packet listener. We use an anonymous inner class for brevity.
PacketListener myListener = new PacketListener() {
public void processPacket(Packet packet) {
// Do something with the incoming packet here.
}
};
// Register the listener.
connection.addPacketListener(myListener, filter);
Smack包含一套丰富的信息包过滤器,你也可以通过信息包过滤器接口(PacketFilter interface)编写程序来创建自己的过滤器。缺省的过滤器集包括:
Ø PacketTypeFilter – 某个特殊的Java类类型的信息包过滤器
Ø PacketIDFilter – 拥有特殊的信息包ID(packet ID)的过滤器 (匹配请求和响应)
Ø ThreadFilter – 拥有特殊线程ID(thread ID)的信息包的过滤器
Ø ToContainsFilter –发送到某个特殊地址的信息包的过滤器 (Group)
Ø FromContainsFilter --发送到某个特殊地址的信息包的过滤器
Ø PacketExtensionFilter – 拥有特殊的信息包扩展的信息包的过滤器
Ø AndFilter –对两个过滤器实施逻辑与操作的过滤器
Ø OrFilter --对两个过滤器实施逻辑或操作的过滤器
Ø NotFilter --对一个过滤器实施逻辑非操作的过滤器
Packet Extensions and Custom IQ’s
Smack提供者架构是为了插件化(Plugin)自定义的XML包扩展和IQ包的系统(The Smack provider architecture is a system for plugging in custom XML parsing of packet extensions and IQ packets)。标准的Smack扩展(Smack Extensions)是使用提供者模型搭建的。存在以下两种类型的提供者:
l IQProvider –将IQ请求( IQ requests)解析成Java对象(Java objects)
l PacketExtension – 将附属在信息包上的XML子文档解析成信息包扩展实例(PacketExtension instances)
默认情况下,Smack只处理如下几个名字空间的IQ信息包(IQ packets):
l jabber:iq:auth
l jabber:iq:roster
l jabber:iq:register
因为许多IQ类型是XMPP及其扩展部分的一部分,所以提供一个可插入的IQ分析机制。IQ Providers被程序自动的注册或通过在你的JAR 文件的META-INF目录下创建一个mack.providers文件。该文件是一个包含一个或多个iqProvider条目(iqProvider entries)的XML文档,如下例所示:
<?xml version="1.0"?>
<smackProviders>
<iqProvider>
<elementName>query</elementName>
<namespace>jabber:iq:time</namespace>
<className>org.jivesoftware.smack.packet.Time</className>
</iqProvider>
</smackProviders>
每一个IQ provider都和一个元素名(elementName)和名字空间( namespace)相联系。在上面的例子中,元素名是query,名字空间是abber:iq:time。如果有多个提供者条目(multiple provider entries)尝试注册并控制相同的名字空间,那么将优先载入类路径(classpath)下的第一个条目。
IQ provider类可以实现IQProvider接口,或者继承IQ类。在前面的例子中,每一个IQProvider负责解析原始的XML流从而创建一个IQ实例。在下面的例子中,bean introspection将被用于尝试自动使用在IQ packet XML中发现的值设置IQ实例的属性。一个XMPP时间信息包如下所示:
<iq type=’result’ to=’[email protected]’ from=’[email protected]’ id=’time_1’>
<query xmlns=’jabber:iq:time’>
<utc>20020910T17:58:35</utc>
<tz>MDT</tz>
<display>Tue Sep 10 12:58:35 2002</display>
</query>
</iq>
为了让这个信息包自动的映射成上面的providers file中所列的时间对象(Time object),它必须有以下几个方法:setUtc(String), setTz(String), 和 setDisplay(String)。自动检查(introspection)的服务将试着自动的将字符串值转化成一个boolean, int, long, float, double,或 Class 类型。转化成何种类型由IQ实例的需要来决定。
信息包插件提供者(Packet extension providers)为信息包提供一个可插入的系统,这些信息包是一个IQ, message和presence packets的自定义名字空间的子元素。每一个插件提供者(extension provider)使用一个元素名(element name)和名字空间(namespace)在smack.providers文件中注册,如下例所示:
<?xml version="1.0"?>
<smackProviders>
<extensionProvider>
<elementName>x</elementName>
<namespace>jabber:iq:event</namespace>
<className>org.jivesoftware.smack.packet.MessageEvent</className>
</extensionProvider>
</smackProviders>
如果有多重提供者条目(multiple provider entries)尝试注册并控制相同的名字空间,那么优先使用类路径(classpath)中载入的第一个条目。
一旦在一个信息包中发现信息包插件,解析器将传递到正确的提供者。每一个提供者可以实现PacketExtensionProvider接口或者是一个标准的Java Bean。在前面的例子中,每一个插件提供者(extension provider)负责解析原始的XML流去构造一个实例。在下面的例子中,bean introspection将被用于尝试自动使用在信息包插件子元素(packet extension sub-element)中的值设置类的属性。
当一个插件提供者(extension provider)没有用元素名(element name)和名字空间(namespace)对注册时,Smack将存储所有在缺省信息包插件(DefaultPacketExtension)对象中的最高级别元素(top-level elements),并匹配到信息包上。
Smack提供简单的机制来将任意的属性附加到信息包上。每一个属性有个字符串类型的名字和一个值,这个值或者是Java原始数据类型(int, long, float, double, boolean),或者是任何可序列化的对象(Serializable object,当一个java对象实现了Serializable接口时,它就是可序列化的)。
所有主要对象都有属性支持,例如消息对象(Message objects)。以下代码阐释了如何设置属性:
Message message = chat.createMessage();
// Add a Color object as a property.
message.setProperty("favoriteColor", new Color(0, 0, 255));
// Add an int as a property.
message.setProperty("favoriteNumber", 4);
chat.sendMessage(message);
获得这些相同的属性要用到以下的代码:
Message message = chat.nextMessage();
// Get a Color object property.
Color favoriteColor = (Color)message.getProperty("favoriteColor");
// Get an int property. Note that properties are always returned as Objects,
// so we must cast the value to an Integer, then convert it to an int.
int favoriteNumber = ((Integer)message.getProperty("favoriteNumber")).intValue();
将对象作为属性值是改变数据的一个非常有力和简单的方法。但是,你应该记住以下几点:
信息包插件(Packet extensions)是向XMPP增加额外数据的更权威的方式。使用属性在某种情况下也许会比较方便,但是,Smack将会控制XML。
Ø 当你将Java对象(Java object)作为属性发送时,只有在客户机运行的Java能够解释数据。所以,可以考虑使用一系列的原始值来传递数据。
Ø 作为属性值发送的对象必须实现序列化接口(Serialiable)。除此之外,发送者和接受者都必须由相同版本的类,否则在反序列化(de-serializing the object)对象时将发生序列化异常。
Ø 序列化的对象将会非常大,将会占用很多的服务器资源和带宽。
当前的用于发送属性的XML格式不是标准的,所以可能不会得到使用Smack的客户的认可。XML如下所示(为了更清晰添加了注释):
<!-- All properties are in a x block. -->
<properties xmlns="http://www.jivesoftware.com/xmlns/xmpp/properties">
<!-- First, a property named "prop1" that’s an integer. -->
<property>
<name>prop1</name>
<value type="integer">123</value>
<property>
<!-- Next, a Java object that’s been serialized and then converted
from binary data to base-64 encoded text. -->
<property>
<name>blah2</name>
<value type="java-object">adf612fna9nab</value>
<property>
</properties>
当前支持的数据类型有:integer, long, float, double, boolean, string, 和java-object(串行化成二进制再进行Base64)。
Smack包含两个内置的调试控制台,他们允许你在服务器和客户机间跟踪XML的踪迹。简单的调试器(lite debugger)是smack.jar的一部分,加强的调试器(enhanced debugger)包含在(smackx-debug.jar)中。可以用两种不同的方法激活调试模式:
1. 在创建连接前加入以下一行代码:
XMPPConnection.DEBUG_ENABLED = true;
2. 将Java的系统属性smack.debugEnabled设置为true。这一系统属性可通过下一命令行设置:
java -Dsmack.debugEnabled=true SomeApp
在你的应用程序中,如果你想明确的禁用调试模式,包括使用命令行参数,则在打开新的连接前在你的应用程序中添加以下一行代码:
XMPPConnection.DEBUG_ENABLED = false;
Smack使用一下的逻辑来决定使用哪个调制控制台:
1. 它将首先尝试使用Java系统属性smack.debuggerClass 所指定的调试类(debugger class)。如果你需要开发自己的调试器 I,可以实现SmackDebugger 接口然后使用下面的命令行设置系统属性:
java -Dsmack.debuggerClass=my.company.com.MyDebugger SomeApp
2. 如果第一步失败了,Smack就会尝试使用增强的调试器(enhanced debugger)。 包含在文件 smackx-debug.jar中 ,因此你要把该jar文件放到类路径(classpath)下。如果空间确定你只是想要配置smack.jar文件,这种情况下增强的调试器(enhanced debugger)将不可用。
3. 最后一种是前面两种都失败后使用简单的调试器(lite debugger)。在你的内存很小的时候,简单的调试器(lite debugger)是一个很好的选择。
当调试模式可用时,将出现一个包含每一个创建的连接的标签调试窗口,该窗口包含以下信息:
Ø 连接标签(Connection tabs) -- 每一个标签显示连接相关的调试信息
Ø Smack信息标签(Smack info tab) -- 显示关于Smack的信息 (例如: Smack的版本(Smack version), 安装的组件(installed components),等等)。
连接标签包含以下信息:
Ø 所有的信息包(All Packets) -- 显示由Smack解析的发送和收到的信息包的信息。
Ø 未经处理的发送信息包(Raw Sent Packets) -- 未经处理的XML traffic(raw XML traffic)由Smack生成并发送至服务器 。
Ø 未经处理的接收信息包(Raw Received Packets) --未经处理的XML traffic(raw XML traffic)由服务器发送给客户机。
Ø Ad-hoc 消息(Ad-hoc message) -- 允许发送各种类型的ad-hoc信息包(ad-hoc packets)。
Ø 信息—显示连接状态和统计信息。
当调试模式可用时,每创建一个连接将出现调试窗口,该窗口包含以下信息:
Ø 客户端的流量(Client Traffic) (红色的文本) --未经处理的XML traffic(raw XML traffic)由Smack生成并发送至服务器 。
Ø 服务器端的流量(Server Traffic)(蓝色的文本) --未经处理的XML traffic(raw XML traffic)由服务器发送给客户机。
Ø 解释的信息包(Interpreted Packets)(绿色的文本) – 显示来自服务器的由Smack解析的XML信息包(XML packets)
右击任何面板会出现一个菜单,上面有一个选项可以将这些内容拷贝到系统的剪贴板或者清除面板中的内容。
万事都不能从零开始,XMPP规范就象webServece规范一样.我们最后选用一个库来加速开发.所以我们选择使用Smack因为它容易使用,设计合理,还记得我们的经典jive吗,它也是他们开发的,应该还是比较不错的. 注意是apache的licence,也就是可以应用到你的商业程序中.
下载地址: http://www.jivesoftware.org/
使用简单,你不需要精通xmpp协议(这加快我们入门,从例子开始我们跟容易理解),创建一个xmpp连接,并向一个用户发送信息,只需要三行代码:
XMPPConnection connection = new XMPPConnection("jabber.org");
connection.login("mtucker", "password");
connection.createChat("[email protected]").sendMessage("Howdy!");
因为是tcp协议,首先必须创建一个网络连接:
XMPPConnection 就是用来创建和xmpp服务器的连接的类,如果需要ssl就使用SSLXMPPConnection.
// 创建连接.
XMPPConnection conn1 = new XMPPConnection("jabber.org");
// 指定端口
XMPPConnection conn2 = new XMPPConnection("jabber.org", 5222);
// 创建ssl连接.
XMPPConnection connection = new SSLXMPPConnection("jabber.org");
一旦你创建了连接,你需要使用用户名和密码来登陆,你可以使用XMPPConnection.login(String username, String password) 方法.一旦你完成登陆了,你可以通过创建新的Chat或者是GroupChat对象来和其它人聊天.
名册让你能够跟踪其它在线的用户,用户可以被组织成为组,例如好友,同事,并且你可以发现谁在线,谁不在线.你可以使用 XMPPConnection.getRoster() 方法来取得好友名单,得到roster类.roster类允许你查找名册实体,比如他们属于那个组,和当前的状态.
读写数据包
每一个从客户端发送到xmpp服务器的包都是xml格式的文本. org.jivesoftware.smack.packet 保护了封装三种xmpp级别包:message,presence,IQ.Chat类和GroupChat类提供了高级别的类管理包自动的生成和发送,但是你也可以手动创建它们.下面例子演示了改变你的在线情况,告诉其它人你不在线"外出捕鱼"去了.
//用presence.Type.UNAVILABLE作为参数创建一个presence描述你不在线.
Presence presence = new Presence(Presence.Type.UNAVAILABLE);
presence.setStatus("Gone fishing");
// 发送该包.
con.sendPacket(presence);
Smack 提供了两种方式来读取包:PacketListener,和 PacketCollector.
它们都使用PacketFilter实例来决定那些包需要处理.一个PacketListener使用事件风格的编程,packet collector 提供轮循和阻塞的操作.
因此,packet listener用来处理随即发送来的包,packet collector用来等待指定的包.
Packet collectors and listeners使用XMPPConnection 对象来创建实例.
发送接受消息是即使通讯软件的核心功能.有两个类帮助完成这个任务.
· org.jivesoftware.smack.Chat -- 用来在两者之间传递信息.
· org.jivesoftware.smack.GroupChat -- 用来加入一个组给很多人之间传递消息.
他们都是使用org.jivesoftware.smack.packet.Message 类来实现消息传递. 在某些环境下你可以绕开Chat GroupChat这两个高级的类手动传递消息或者是加入listener.
一个Chat对象在两个用户之间使用一个thread ID创建一个新的消息线程.下面的代码演示了如何使用一个user创建一个新的Chat,然后向它发送文本消息.
// 假设我们创建了一个 XMPPConnection 对象 名称为 "connection".
Chat newChat = connection.createChat("[email protected]");
newChat.sendMessage("Howdy!");
实际上Chat.sendMessage(String) 方法是一个我们提供的创建一个Message对象的方便的方法,它使用String 参数作为body,然后发送这个message.如果你想要加入其它熟悉,请使用:
// 假设我们创建了一个 XMPPConnection 对象 名称为 "connection".
Chat newChat = connection.createChat("[email protected]");
Message newMessage = newChat.createMessage();
newMessage.setBody("Howdy!");
message.setProperty("favoriteColor", "red");
newChat.sendMessage(newMessage);
Chat对象容许你侦听其它chat的回应.下面的代码实现了鹦鹉学舌--它会对任何人的话做出同样的回答.
// 假设我们创建了一个 XMPPConnection 对象 名称为 "connection".
Chat newChat = connection.createChat("[email protected]");
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()方法来得到下一个message,它是阻塞式的.当然你可以加入一个listener来获取消息.
一个group chat运行你同时和一群用户聊天.你必须使用一个昵称来加入一个聊天组.下面代码演示了如何加入聊天组,并且发送消息.
// 假设我们创建了一个 XMPPConnection 对象 名称为 "connection".
GroupChat newGroupChat = connection.createGroupChat("[email protected]");
// 使用昵称 "jsmith"加入聊天组.
newGroupChat.join("jsmith");
// 发送消息给聊天组.
newGroupChat.sendMessage("Howdy!");
发送和接受消息和Chat很类似就不详细讲述了.
名单允许你跟踪其它用户的状态并且把用户分为好友,同事等等.
你可以通过XMPPConnection.getRoster()方法获取Roster对象,但是必须在你成功登陆后.
Roster 实体每一个用户都被描述为一个Roster实体,它包包括:
· 一个 XMPP 地址 (e.g. [email protected]).
· 名称,昵称 (e.g. "Joe").
· 它属于哪个列表,如好友,同事?
下面的代码打印出所有Roster实体的信息:
Roster roster = con.getRoster();
for (Iterator i=roster.getEntries(); i.hasNext(); ) {
System.out.println(i.next());
}
我们也提供了取得特定实体的方法,或者是组的方法.
每一个用户列表中的对象都有在线状态.Roster.getPresence(String user)返回一个Presence对象描述用户的在线状态.
一个用户的presence要么是在线要么是离线.当一个用户在线时候,它他们的presence还可以包含一些额外的信息,例如当前他们在做什么,他们是否乐于别打扰.
一般的来说都是用一个tree来显好友,例如,你看看下面的图:
presence信息通常都是随时变化的,当然也有可能名单的增加和删除.要监听名单和presence的改变,必须使用RosterListener .下面的代码演示了如何监听并把任何presence改变打印出来.通常客户端软件有显示的代码来完成更新用户名单UI的代码来反映变化.
final Roster roster = con.getRoster();
roster.addRosterListener(new RosterListener() {
public void rosterModified() {
// Ignore event for this example.
}
public void presenceChanged(String user) {
// If the presence is unavailable then "null" will be printed,
// which is fine for this example.
System.out.println("Presence changed: " + roster.getPresence(user));
}
});
用户列表使用的允许模型,也就是只有对方容许你才能把他加入到你的用户名单中.这个保护了用户的隐私.因此,你加入一个用户将会进入一个未决状态直到对方接受请求.
同时如果有一个用户请求把你加入,你必须对这个请求作出接受或反对的回应.Smack提供了三种策略:
· 自动接受所有请求.
· 自动反对所有请求.
· 交给用户手动处理.
你可以通过Roster.setSubscriptionMode(int subscriptionMode)来设置使用哪种策略.一个简单的客户端可以使用自动同意模式,但是一个功能比较健全的客户端应该提供用户手动出来.在手动模式中,你必须提供一个PacketListener 注册来诊听含有类型Presence.Type.SUBSCRIBE的Presence 包.
Smack提供了一个灵活的框架来处理接受到的数据包:
· org.jivesoftware.smack.PacketCollector -- 让你同步等待新的包.
· org.jivesoftware.smack.PacketListener -- 异步通知你包的到来.
一个packet listener在事件风格的程序中使用,packet collector可以做轮循和阻塞操作.它们都可以使用XMPPConnection 实例来创建.
org.jivesoftware.smack.filter.PacketFilter 接口定义了那些包会分发到PacketCollector 或者是PacketListener.你可以在 org.jivesoftware.smack.filter 包中找到预定义的filters.
// 创建一个包 filter监听特定用户的消息.我们使用AndFilter来联合两个filters.
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class),
new FromContainsFilter("[email protected]"));
// 假设我们创建了名称为 "connection" XMPPConnection 对象.
// 首先使用上面的filter创建PacketCollector对象.
PacketCollector myCollector = connection.createPacketCollector(filter);
// 接着, 我们创建一个 packet listener. 方便起见我们使用匿名内部类.
PacketListener myListener = new PacketListener() {
public void processPacket(Packet packet) {
// 当包到的时候你做什么.
}
};
// 注册.
connection.addPacketListener(myListener, filter);
我们提供了一系列的filters,你可以实现 PacketFilter 接口来自定义实现.
Ø PacketTypeFilter -- filters for packets that are a particular Class type.
Ø PacketIDFilter -- filters for packets with a particular packet ID.
Ø ThreadFilter -- filters for message packets with a particular thread ID.
Ø ToContainsFilter -- filters for packets that are sent to a particular address.
Ø FromContainsFilter -- filters for packets that are sent to a particular address.
Ø PacketExtensionFilter -- filters for packets that have a particular packet extension.
Ø AndFilter -- implements the logical AND operation over two filters.
Ø OrFilter -- implements the logical OR operation over two filters.
Ø NotFilter -- implements the logical NOT operation on a filter.
用Smack编写jabber客户端的基本知识都够了,更多内容请查阅官方资料和mail list请求帮助.