Guide to XMPP Smack Client – XMPP Smack客户端指南

最后修改: 2019年 1月 25日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

XMPP is a rich and complex instant messaging protocol.

XMPP是一个丰富而复杂的即时通信协议。

Instead of writing our own client from scratch, in this tutorial, we’ll take a look at Smack, a modular and portable open source XMPP client written in Java that has done much of the heavy lifting for us.

在本教程中,我们不需要从头开始编写我们自己的客户端,而是看看Smack,一个用Java编写的模块化和可移植的开源XMPP客户端,它已经为我们完成了大部分的重任。

2. Dependencies

2.依赖性

Smack is organized as several modules to provide more flexibility, so we can easily include the features we need.

Smack被组织成几个模块,以提供更多的灵活性,因此我们可以轻松地包括我们需要的功能。

Some of these include:

其中一些包括。

  • XMPP over TCP module
  • A module to support many of the extensions defined by the XMPP Standards Foundation
  • Legacy extensions support
  • A module to debug

We can find all the supported modules in XMPP’s documentation.

我们可以在XMPP的文档中找到所有支持的模块。

However, in this tutorial, we’ll just make use of the tcp, imextensions, and java7 modules:

然而,在本教程中,我们将只利用tcpimextensionsjava7模块。

<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-tcp</artifactId>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-im</artifactId>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-extensions</artifactId>
</dependency>
<dependency>
    <groupId>org.igniterealtime.smack</groupId>
    <artifactId>smack-java7</artifactId>
</dependency>

The latest versions can be found at Maven Central.

最新版本可在Maven中心找到。

3. Setup

3.设置

In order to test the client, we’ll need an XMPP server. To do so, we’ll create an account on jabber.hot-chilli.net, a free Jabber/XMPP service for everybody.

为了测试该客户端,我们需要一个XMPP服务器。为此,我们将在jabber.hot-chilli.net上创建一个账户,这是一个面向所有人的免费Jabber/XMPP服务。

Afterward, we can configure Smack using the XMPPTCPConnectionConfiguration class that provides a builder to set up the connection’s parameters:

之后,我们可以使用XMPPTCPConnectionConfiguration类来配置Smack,该类提供了一个构建器来设置连接的参数。

XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
  .setUsernameAndPassword("baeldung","baeldung")
  .setXmppDomain("jabb3r.org")
  .setHost("jabb3r.org")
  .build();

The builder allows us to set the basic information needed to perform a connection. If needed, we can also set other parameters such as port, SSL protocols, and timeouts.

该构建器允许我们设置执行连接所需的基本信息。如果需要,我们还可以设置其他参数,如端口、SSL协议和超时。

4. Connection

4.连接

Making a connection is simply achieved using the XMPPTCPConnection class:

使用XMPPTCPConnection类就可以简单地建立连接。

AbstractXMPPConnection connection = new XMPPTCPConnection(config);
connection.connect(); //Establishes a connection to the server
connection.login(); //Logs in

The class contains a constructor that accepts the configuration previously built. It also provides methods to connect to the server and log in.

该类包含一个构造函数,接受先前建立的配置。它还提供了连接到服务器和登录的方法。

Once a connection has been established, we can use Smack’s features, like chat, that we’ll describe in the next section.

一旦建立了连接,我们就可以使用Smack的功能,比如聊天,我们将在下一节介绍。

In the event that the connection was suddenly interrupted, by default, Smack will try to reconnect.

在连接突然中断的情况下,默认情况下,Smack将尝试重新连接。

The ReconnectionManager will try to immediately reconnect to the server and increase the delay between attempts as successive reconnections keep failing.

ReconnectionManager将尝试立即重新连接到服务器,并在连续的重新连接不断失败时增加尝试的延迟。

5. Chat

5.聊天

One of the major features of the library is – chat support.

该库的主要功能之一是–聊天支持。

Using the Chat class makes possible to create a new thread of messages between two users:

使用Chat类,可以在两个用户之间创建一个新的消息线程。

ChatManager chatManager = ChatManager.getInstanceFor(connection);
EntityBareJid jid = JidCreate.entityBareFrom("baeldung2@jabb3r.org");
Chat chat = chatManager.chatWith(jid);

Note that, to build a Chat we used a ChatManager and, obviously, specified who to chat with. We achieved the latter by using the EntityBareJid object, which wraps an XMPP address —aka a JID— composed of a local part (baeldung2) and a domain part (jabb3r.org).

请注意,为了建立一个Chat,我们使用了一个ChatManager,而且很明显,我们指定了要与谁聊天。我们通过使用EntityBareJid对象来实现后者,该对象包裹了一个XMPP地址–即JID–,由本地部分(baeldung2)和域名部分(jabb3r.org)组成。

After that, we can send a message using the send() method:

之后,我们可以使用send()方法发送一个消息。

chat.send("Hello!");

And receive messages by setting a listener:

并通过设置一个监听器来接收信息。

chatManager.addIncomingListener(new IncomingChatMessageListener() {
  @Override
  public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
      System.out.println("New message from " + from + ": " + message.getBody());
  }
});

5.1. Rooms

5.1.客房

As well as end-to-end user chat, Smack provides support for group chats through the use of rooms.

除了端到端的用户聊天,Smack还通过使用房间为小组聊天提供支持

There are two types of rooms, instant rooms, and reserved rooms.

有两种类型的房间,即期房间和预约房间。

Instant rooms are available for immediate access and are automatically created based on some default configuration. On the other hand, reserved rooms are manually configured by the room owner before anyone is allowed to enter.

即时房间可供立即访问,并根据一些默认配置自动创建。另一方面,保留房间是由房间所有者在允许任何人进入之前手动配置的。

Let’s have a look at how to create an instant room using MultiUserChatManager:

让我们看看如何使用MultiUserChatManager创建一个即时房间。

MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);
MultiUserChat muc = manager.getMultiUserChat(jid);
Resourcepart room = Resourcepart.from("baeldung_room");
muc.create(room).makeInstant();

In a similar fashion we can create a reserved room:

以类似的方式,我们可以创建一个保留房间。

Set<Jid> owners = JidUtil.jidSetFrom(
  new String[] { "baeldung@jabb3r.org", "baeldung2@jabb3r.org" });

muc.create(room)
  .getConfigFormManger()
  .setRoomOwners(owners)
  .submitConfigurationForm();

6. Roster

6.名册

Another feature that Smack provides is the possibility to track the presence of other users.

Smack提供的另一个功能是可以跟踪其他用户的存在。

With Roster.getInstanceFor(), we can obtain a Roster instance:

通过Roster.getInstanceFor(),我们可以获得一个Roster实例。

Roster roster = Roster.getInstanceFor(connection);

The Roster is a contact list that represents the users as RosterEntry objects and allows us to organize users into groups.

Roster是一个联系人列表,它将用户表示为RosterEntry对象,并允许我们将用户组织成小组。

We can print all entries in the Roster using the getEntries() method:

我们可以使用getEntries()方法打印Roster中的所有条目。

Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
    System.out.println(entry);
}

Moreover, it allows us to listen for changes in its entries and presence data with a RosterListener:

此外,它允许我们通过RosterListener:监听其条目和存在数据的变化。

roster.addRosterListener(new RosterListener() {
    public void entriesAdded(Collection<String> addresses) { // handle new entries }
    public void entriesDeleted(Collection<String> addresses) { // handle deleted entries }
    public void entriesUpdated(Collection<String> addresses) { // handle updated entries }
    public void presenceChanged(Presence presence) { // handle presence change }
});

It also provides a way to protect user’s privacy by making sure that only approved users are able to subscribe to a roster. To do so, Smack implements a permissions-based model.

它还提供了一种保护用户隐私的方法,确保只有经过批准的用户才能订阅名册。为此,Smack实施了一个基于权限的模型。

There are three ways to handle presence subscription requests with the Roster.setSubscriptionMode() method:

有三种方法可以用Roster.setSubscriptionMode()方法处理存在订阅请求。

  • Roster.SubscriptionMode.accept_all – Accept all subscription requests
  • Roster.SubscriptionMode.reject_all – Reject all subscription requests
  • Roster.SubscriptionMode.manual – Process presence subscription requests manually

If we choose to handle subscription requests manually, we’ll need to register a StanzaListener (described in next section) and handle packets with the Presence.Type.subscribe type.

如果我们选择手动处理订阅请求,我们将需要注册一个StanzaListener(在下一节中描述),并处理具有Presence.Type.subscribe类型的数据包。

7. Stanza

7.房间

In addition to the chat, Smack provides a flexible framework to send a stanza and listen for incoming one.

除了聊天之外,Smack还提供了一个灵活的框架,可以发送一个段子并监听传入的段子。

To clarify, a stanza is a discrete semantic unit of meaning in XMPP. It is structured information that is sent from one entity to another over an XML stream.

澄清一下,在XMPP中,节是一个离散的意义语义单位。它是通过一个XML流从一个实体发送到另一个实体的结构化信息。

We can transmit a Stanza through a Connection using the send() method:

我们可以通过Connection使用send()方法传输Stanza

Stanza presence = new Presence(Presence.Type.subscribe);
connection.sendStanza(presence);

In the example above, we sent a Presence stanza to subscribe to a roster.

在上面的例子中,我们发送了一个Presence节来订阅一个名册。

On the other hand, to process the incoming stanzas, the library provides two constructs:

另一方面,为了处理传入的句子,该库提供了两个结构。

  • StanzaCollector 
  • StanzaListener

In particular, StanzaCollector let us wait synchronously for new stanzas:

特别是,StanzaCollector让我们同步地等待新的句子

StanzaCollector collector
  = connection.createStanzaCollector(StanzaTypeFilter.MESSAGE);
Stanza stanza = collector.nextResult();

While StanzaListener is an interface for asynchronously notifying us of incoming stanzas:

StanzaListener是一个接口,用于异步通知我们进入的句子

connection.addAsyncStanzaListener(new StanzaListener() {
    public void processStanza(Stanza stanza) 
      throws SmackException.NotConnectedException,InterruptedException, 
        SmackException.NotLoggedInException {
            // handle stanza
        }
}, StanzaTypeFilter.MESSAGE);

7.1. Filters

7.1.滤波器

Moreover, the library provides a built-in set of filters to process incoming stanzas.

此外,该库提供了一套内置的过滤器来处理传入的句子。

We can filter stanza by type using StanzaTypeFilter or by ID with StanzaIdFilter:

我们可以用StanzaTypeFilter按类型过滤节段,或者用StanzaIdFilter:按ID过滤节段。

StanzaFilter messageFilter = StanzaTypeFilter.MESSAGE;
StanzaFilter idFilter = new StanzaIdFilter("123456");

Or, discerning by particular address:

或者,通过特定的地址进行辨别。

StanzaFilter fromFilter
  = FromMatchesFilter.create(JidCreate.from("baeldung@jabb3r.org"));
StanzaFilter toFilter
  = ToMatchesFilter.create(JidCreate.from("baeldung2@jabb3r.org"));

And we can use logical filter operator (AndFilter, OrFilter, NotFilter) to create complex filters:

而且我们可以使用逻辑过滤器操作符(AndFilter, OrFilter, NotFilter)来创建复杂的过滤器。

StanzaFilter filter
  = new AndFilter(StanzaTypeFilter.Message, FromMatchesFilter.create("baeldung@jabb3r.org"));

8. Conclusion

8.结论

In this article, we covered the most useful classes that Smack provides off the shelf.

在这篇文章中,我们介绍了Smack提供的最有用的类的现成资料。

We learned how to configure the library in order to send and receive XMPP stanza.

我们学习了如何配置这个库,以便发送和接收XMPP的句子。

Subsequently, we learned how to handle group chats using ChatManager and Roster features.

随后,我们学习了如何使用ChatManagerRoster功能处理群聊。

As usual, all code samples shown in this tutorial are available over on GitHub.

像往常一样,本教程中显示的所有代码样本都可以在GitHub上找到