【XMPP】Smack源码之初步认识

Smack 概述

  Smack是一个用于和XMPP服务器通讯的类库,由此能够实现即时通信和聊天。java

Smack主要优点

  • 很是简单易用,而且有十分强大的 API。只需三行代码就能够向用户发关文本消息: 
    XMPPConnection connection = new XMPPConnection("jabber.org");
    connection.login("mtucker", "password");
    connection.createChat("jsmith@jivesoftware.com").sendMessage("Howdy!");
    View Code
  • 不像其它类库那样强制您进行包级别的编码。Smack提供智能的更高级的构造,像 ChatGroupChat类,让您进行高效的编程。
  • 不需用您熟悉XMPP XML格式,即便您熟悉 XML.
  • 提供简单的设计以进行通信,Smack容许您在每一个消息中设置任意数量的属性,包括java对象。
  • Apache许可下的开源类库,这意味着您能够将Smack整合进您的商业或非商业的应用中。 

必要条件

  Smack的惟一必要条件是JDK 1.2 或更高版本。smack.jar文件已包含一个XML解析器,不须要其它第三方类库。编程

创建链接

  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"); 
View Code

  一旦您创建了一个链接,您必须经过方法XMPPConnection.login(String username, String password)使用用户名和密码登录
框架

  若是登录成功,您能够经过建立新的Chat或GroupChat对象和其它用户聊天。  异步

操做Roster 

  Roster可以让您跟踪其它用户的有效性(存在)ide

  您能够经过使用像“朋友”和“同事”这样的组来组织用户,这样您能够发现每一个用户是否在线。this

  使用XMPPConnection.getRoster()这个方法获得Roster。编码

  经过Roster类您能够找到全部Roster登录、他们所属的组以及每一个登录当前的存在状态。  spa

读写Packet 

  从客户端以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);
View Code 

  Smack提供两种方法读取收到的packet:

  1. PacketListener[packet监听器]PacketCollector[packet收集器]。
  2. 两者都是使用PacketFilter实例来决定哪一个packet应该被处理。
  3. packet监听器用于事件样式的编程,而packet收集器有一个能够作轮询和阻塞操做的packet的结果队列。
  4. 因此,当您想对一个有可能随时到来的packet采起一些操做时,使用packet监听器;
  5. 而当您想等待一个特别的packet到来时,使用packet收集器。
  6. 可使用XMPPConnection实例建立packet收集器和监听器。  

使用Chat 和 GroupChat发送消息

  往复地发送消息处于即时通信的核心地位。

  两个类辅助发送和接收消息: 

    • org.jivesoftware.smack.Chat --用于在两我的之间发送消息。
    • org.jivesoftware.smack.GroupChat --用于加入聊天室在多我的之间发送消息。

  ChatGroupCha类都是使用 org.jivesoftware.smack.packet.Message packet类来发送消息。

  在某种特定状况下,您可能不肯意使用高级的Chat和GroupChat类而直接发送和监听消息。 

Chat 

  一个chat在两个用户之间建立一个消息线程(经过线程ID)。

  下面这段代码演示了怎样和用户建立一个新的Chat并向他们发送一条文本消息:

// 假设咱们已经建立了一个名为"connection"的XMPPConnection。
Chat newChat = connection.createChat("jsmith@jivesoftware.com");
newChat.sendMessage("Howdy!");
View Code

  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);
View Code

  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());
}
View Code

  以上这段代码使用了这个Chat.nextMessage() 方法获得下一条消息,它将等待不肯定什么时候到来的另外一条消息。

  固然也有其它的方法用于等待特定时间段到来的新消息,或者您能够添加一个监听器,它将在每次有消息到来时通知您。  

GroupChat 

  经过GroupChat链接到服务器上的聊天室,您能够在一群人中发送和接收消息。但在您发送或接收消息以前,您必须用一个昵称加入聊天室。

  下面这段代码演示了链接到一个聊天室并发送一条消息:

// 假设咱们已经建立了一个名为"connection"的XMPPConnection。
GroupChat newGroupChat = connection.createGroupChat("test@jivesoftware.com");
// 用昵称"jsmith"加入这处群。
newGroupChat.join("jsmith");
// 向聊天室中的其它人发送一条消息。
newGroupChat.sendMessage("Howdy!");
View Code

  一般,在群中发送和接收消息和在Chat类中很是类似。

  同时还提供了用于获得聊天室中其它人的列表的方法。 


处理收到的Packet

  Smack提供灵活的框架来经过两种构造处理收到的 packet:

    • org.jivesoftware.smack.PacketCollector —— 一个让您同步等待新packet的类。
    • org.jivesoftware.smack.PacketListener —— 一个异步通知您引入的packet的接口。  

  packet监听器用于事件样式的编程,而packet收集器有一个能够作轮询和阻塞操做的packet的结果队列。

  因此,当您想对一个有可能随时到来的packet采起一些操做时,使用packet监听器;而当您想等待一个特别的packet到来时,使用packet收集器。

  您可使用XMPPConnection实例建立packet收集器和监听器。

  org.jivesoftware.smack.filter.PacketFilter 接口决定哪一个特别的将会被传递到PacketCollectorPacketListener。

  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。

// 接下来,建立一个packet监听器。咱们能够简便地使用匿名内部类。
PacketListener myListener = new PacketListener() {
        public void processPacket(Packet packet) {
            // 在这里用收到的packet作些什么。
        }
    };
// 注册这个监听器。
connection.addPacketListener(myListener, filter);
View Code

标准Packet过滤器

  Smack包括丰富的packet 过滤器集,固然您能够经过实现PacketFilter接口建立本身的过滤器。

   默认的过滤器集包括:

    • PacketTypeFilter ——特定类的packet过滤器。
    • PacketIDFilter ——含有特定packet ID的packet过滤器。
    • ThreadFilter ——含有特定线程ID的消息packet过滤器。
    • ToContainsFilter ——发送到特定地址的packet过滤器。
    • FromContainsFilter ——来自特定地址的packet过滤器。
    • PacketExtensionFilter ——含有特定packet扩充的packet过滤器 filters for s that have a particular  extension.
    • AndFilter ——实现两个过滤器的逻辑“与”操做。
    • OrFilter —— 实现两个过滤器的逻辑“或”操做。
    • NotFilter ——实现一个过滤器的逻辑“非”操做。

Packet属性

  Smack提供一个有效的机制,能够向packet附加任意属性。

  每一个属性有一个String名字,这是一个java简单类型值(int, long, float, double, boolean)and a value that is a Java primitive () 或者任何序列化对象(java对象可序列化当它实现了Serializable接口)。 

使用API

  全部主要对象支持属性,如Message对象。

  下面的代码显示如何设置属性: 

Message message = chat.createMessage();
// 添加一个Color对象做为属性。
message.setProperty("favoriteColor", new Color(0, 0, 255));
// 添加一个int做为属性。
message.setProperty("favoriteNumber", 4);
chat.sendMessage(message);
View Code

  使用以下代码得到这些属性: 

Message message = chat.nextMessage();
// 得到一个Color对象属性。
Color favoriteColor = (Color)message.getProperty("favoriteColor");
// 得到一个intg属性,注意属性做为对象返回,咱们必须把值转换为Integer,而后转换为int。
int favoriteNumber = ((Integer)message.getProperty("favoriteNumber")).intValue();
View Code

对象做为属性 

  使用对象做为属性值是一个很是强大和容易的交换数据的方式。

  然而,应该牢记以下: 

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

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> 
View Code

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


roster和presence

  roster能让您跟踪其它用户的有效性(存在)。您能够经过使用像“朋友”和“同事”这样的组来组织用户。  

  其它IM系统如朋友列表,联系列表引用roster。

  一个roster实例经过XMPPConnection.getRoster()方法得到,但仅当成功登录服务器以后对可用。

名薄登录

  在roster中每一个用户用一个RosterEntry表示,它包括:

    • 一个XMPP地址(例如 jsmith@example.com)。
    • 一个您分配给用户的名字(例如 "Joe")。
    • 登录所属的roster组列表。若是roster登录不属于任何组,它将被称为“unfiled entry”。 

  下面的代码片断打印roster中的全部登录:

Roster roster = con.getRoster();
for (Iterator i=roster.getEntries(); i.hasNext(); ) {
    System.out.println(i.next());
}
View Code

  也可能用方法得到单个登录,未定义登录列表,或者得到一个或全部roster组。

presence

  roster中的每一个登录有presence与之关联。

  Roster.getPresence(String user)方法能够返回一个用户Presence的对象,若是用户不在线或您没有预订用户的presence将会返回null。

  注意:通常而言,presence预订通常受用户是否在roster中的约束,但这并不适应全部状况。

  一个用户能够有在线或离线两种presence。

  当用户在线时,他们的可能包含外延信息,如他们正在作什么,他们是否愿意被打扰等等。参考Presence类以得到更多细节信息。

监听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));
    }
});
View Code

向roster中添加登录 

  roster和presence使用一种基于许可的模式,用户只有在被许可的状况下才能被添加到别人的roster中。

  这样能够保护用户的隐私由于只有经核准的其它用户才能查看他们的 presence信息。

  所以,只有当其它用户接受您的请求时您才能添加新的roster登录。

  若是一个用户请求presence预订,所以他们能够把您添加到他们的roster中,您必须接受或拒绝该请求。

  Smack经过如下三种方式中的一种处理presence预订请求: 

    • 自动接受全部presence预订请求。
    • 自动拒绝全部presence预订请求。
    • 手动处理presence预订请求。

  经过Roster.setSubscriptionMode(int subscriptionMode)方法设置对请求的处理方式。

  简易客户端一般使用一种自动方式处理预订请求,而复杂客户端应该手动处理方式,请最终用户接受或拒绝请求。

  若是使用手动方式,应该注册一个PacketListener以监听Presence.Type.SUBSCRIBE类型的Presence packet。


Privacy

Privacy是什么

  Privacy是用户阻挡其它个别用户的通讯的方法。在XMPP中它经过操做隐私列表完成。
  经过下面的用例服务器端隐私列表可以成功完成:  

  • 从新得到某人的隐私列表。
  • 添加,删除,并编辑某人的隐私列表。
  • 设置,更改,或放弃活动列表。
  • 设置,更改,或放弃默认列表(也就是默认活动的列表)。
  • 基于JID,组,或签名类型(或所有)容许或阻挡消息。
  • 基于JID,组,或签名类型(或所有)容许或阻挡向内的出席通知。
  • 基于JID,组,或签名类型(或所有)容许或阻挡向外的出席通知。
  • 基于JID,组,或签名类型(或所有)容许或阻挡IQ节。
  • 基于JID,组,或签名类型(或所有)容许或阻全部通讯。

怎么使用它

  API实现有三个主公共类: 

  • PrivacyListManager: 这是从新得到并处理服务器隐私列表的主API类。
  • PrivacyList: 表明一个隐私列表,有一个名字,一组隐私项目。例如,可见或不可见列表。
  • PrivacyItem:阻挡或容许隐私的某个方面。例如,容许个人的朋友看到咱们的出席状态。 

  1.正确从头开始,客户端能够得到他的/她的存储在服务器上的隐私列表:

// 为当前链接建立一个隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
// 从新得到服务器隐私列表。
    PrivacyList[] lists = privacyManager.getPrivacyLists();
View Code

   正在客户端可以显示服务器的每一个PrivacyItem和每一个列表是不是活动的,默认或没有。客户端是一个隐私变化的监听器。

  2.要向服务器添加一个新列表,客户端能够像这样执行:

// 设置列表的名称
    String listName = "newList";

    // 建立PrivacyItem的列表,PrivacyItem将会容许或拒绝某些隐私方面。
    String user = "tybalt@example.com";
    String groupName = "enemies";
    ArrayList privacyItems = new ArrayList();

    PrivacyItem item = new PrivacyItem(PrivacyRule.JID, true, 1);
    item.setValue(user);
    privacyItems.add(item);

    item = new PrivacyItem(PrivacyRule.SUBSCRIPTION, true, 2);
    item.setValue(PrivacyRule.SUBSCRIPTION_BOTH);
    privacyItems.add(item);
         
    item = new PrivacyItem(PrivacyRule.GROUP, false, 3);
    item.setValue(groupName);
    item.setFilterMessage(true);
    privacyItems.add(item);

    // 得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 建立新列表。
    privacyManager.createPrivacyList(listName, Arrays.asList(privacyItems));
View Code

  3.修改一个已存的列表,客户端代码可能像这样: 

// 设置列表名称
    String listName = "existingList";
    //得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 向服务器发送新列表。
    privacyManager.updatePrivacyList(listName, items);
View Code

  注意items在例2中定义而且必须包含列表中的全部元素(not the "delta")。 

  4.删除一个已存在的列表,客户端能够像这样执行: 

// 设置列表名称
    String listName = "existingList";
    // 得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 删除列表。
    privacyManager.deletePrivacyList(listName);
View Code

   5.放弃活动列表的使用,客户端能够像这样执行:

// 得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 放弃活动列表的使用。
    privacyManager.declineActiveList();
View Code

  6.放弃默认列表的使用,客户端能够像这样执行: 

// 得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 放弃默认列表的使用。
    privacyManager.declineDefaultList();
View Code

监听Privacy变化

  为了处理隐私变化,客户端应该监听管理器的更新。

  当一个列表更改时管理器通知每一个已添加的监听器。

  监听必须实现PrivacyListListener接口。当隐私列表被修改时客户端可能须要做出反应。

  PrivacyListManager让您添加监听器,它将在列表被改变时得知通知。监听器应该实现PrivacyListListener接口
  最重要的通知是updatedPrivacyList,它当隐私列表改变它的隐私项目时被执行。
  当执行以下代码监听器能获得通知:  

// 得到当前链接的隐私管理器。
    PrivacyListManager privacyManager = PrivacyListManager.getInstanceFor(myConnection);
    // 为了获得通知添加监听器(this)
    privacyManager.addListener(this);
View Code
相关文章
相关标签/搜索