通过 Smack 4.2.0 建立 XMPP 连接时出现 NullPointerException

NullPointerException when making XMPP connection through Smack 4.2.0

我打算使用 Smack 通过 Firebase CCS 发送消息。我为我的 Web 应用程序修改了一个简单的 CCS 客户端,但是当我尝试建立连接时,它导致异常。 我正在使用 Smack 4.2.0

这里是连接的过程

1) 我客户端的连接方式:

public void connect() throws XMPPException{
        try{
            config = XMPPTCPConnectionConfiguration.builder()
                    .setPort(Config.FCM_PORT)
                    .setHost("fcm-xmpp.googleapis.com")
                    .setXmppDomain("googleapis.com")
                    .setSecurityMode(/*Default; Explicit setting for emphasis*/SecurityMode.ifpossible)
                    .setSendPresence(true)
                    .setUsernameAndPassword(fcmServerUsername, mApiKey)
                    .setSocketFactory(SSLSocketFactory.getDefault())
                    .setDebuggerEnabled(mDebuggable)/* Launch a window with info about packets sent and received */
                    .build();
        }
        catch(XmppStringprepException e){
            e.printStackTrace();
        }

        connection = new XMPPTCPConnection(config);


        // Configuring Automatic reconnection
        ReconnectionManager manager = ReconnectionManager.getInstanceFor(connection);
        manager.setReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY);
        manager.enableAutomaticReconnection();

        // Connect now then login
        try{
            connection.connect();
            connection.login();
        }
        // TODO: Handle the exceptions if possible appropriately
        catch(SmackException sme){
            logger.severe(sme.getMessage());
            sme.printStackTrace();
        }
        catch(IOException ioe){
            logger.severe(ioe.getMessage());
            ioe.printStackTrace();
        }
        catch(InterruptedException ie){
            logger.severe("Connection got interrupted!!");
            ie.printStackTrace();
        }
    }

2) 我跟踪了异常并在此处找到它:(Smack 的来源)

在行 - HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());

// AbstractXMPPConnection.java
protected List<HostAddress> populateHostAddresses() {
        List<HostAddress> failedAddresses = new LinkedList<>();
        if (config.hostAddress != null) {
            hostAddresses = new ArrayList<>(1);
            HostAddress hostAddress = new HostAddress(config.port, config.hostAddress);
            hostAddresses.add(hostAddress);
        }
        else if (config.host != null) {
            hostAddresses = new ArrayList<HostAddress>(1);
            HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
            if (hostAddress != null) {
                hostAddresses.add(hostAddress);
            }
        } else {
            // N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
            hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode());
        }
        // Either the populated host addresses are not empty *or* there must be at least one failed address.
        assert(!hostAddresses.isEmpty() || !failedAddresses.isEmpty());
        return failedAddresses;
    }

异常是 NullPointerException,我发现 getDNSResolver() returns 为空。在我引用的所有来源中,没有任何与 DNS 解析器相关的内容,因为它应该由 Smack 内部处理。所以我的问题是,我是否错过了一些关键的配置或建立连接的步骤?

编辑: 我在这里问是因为 Smack 是一个庞大的库,可能有人知道我可能错过了一些配置。我无法直接设置 DNSResolver

编辑:更新答案

这不是 Smack 源代码中的错误,因为他们的 4.2.0 升级指南明确提到:

**

API Changes

**

Warning: This list may not be complete

Introduced ConnectionConfiguration.setHostAddress(InetAddress)

In previous versions of Smack, ConnectionConfiguration.setHost(String) could be used to set the XMPP service's host IP address. This is no longer possible due to the added DNSSEC support. You have to use the new connection configuration ConnectionConfiguration.setHostAddress(InetAddress) instead.


这似乎是一个错误,因为我通过提供主机地址(应该是从 {Host , 领域})。那么,我怎么知道要提供主机地址呢?

诀窍就在这里:(Smack 来源)

// AbstractXMPPConnection.java

if (config.hostAddress != null) {
            hostAddresses = new ArrayList<>(1);
            HostAddress hostAddress = new HostAddress(config.port, config.hostAddress);
            hostAddresses.add(hostAddress);
        }
        else if (config.host != null) {
            hostAddresses = new ArrayList<HostAddress>(1);
            HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
            if (hostAddress != null) {
                hostAddresses.add(hostAddress);
            }
        } else {
            // N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
            hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode());
        }

您可以在此处看到 if、else-if 块,并且由于异常出现在 else if (config.host != null) 块中,我提供了 hostAddress 以便它永远不会进入该块并且它起作用了。

我知道这是对实际问题的一种破解,但这似乎是 Smack 4.2.0 中的一个错误,除非有人反驳我。

奖励信息:如果在纠正此问题后,在登录过程中出现 Base 64 编码的另一个异常,请参考此 - XMPP client using Smack 4.1 giving NullPointerException during login

在 4.2.0 中不确定,但在 4.2.2(和更新版本)中,您将需要 smack-resolver-dnsjava-4.2.2.jar 才能在您的 class 路径中,smack 调用包中包含的 DNSUtil,如果 class 不存在它 returns NullPointerException.

希望对您有所帮助! 大卫