什么时候断开bosh连接建立从app服务器到strophe使用预绑定?

时间:2014-08-22 21:16:03

标签: java servlets smack strophe bosh

这个问题是关于这个问题“How to connect XMPP bosh server using java smack library?

的前一个问题的扩展

我使用Java作为服务器端语言。我已经使用smach-jbosh成功实现了xmpp BOSH连接,感谢@Deuteu帮助我实现这一目标,到目前为止,我修改了jbosh的BOSHClient.java文件并添加了两个getter方法来提取RID和SID。

现在我的app服务器上有RID和SID(我正在使用Apache Tomcat)。我需要将此凭证传递给Strophe(Web客户端),以便它可以附加到连接。

我有一些疑问。

      
  1. 何时断开bosh连接从应用服务器建立?在传递sid之前,摆脱和jid到strophe或在传过sid之后,摆脱和jid strophe?
       根据我在实施过程中的观察结果,我观察到,一旦来自应用服务器的波什连接断开,会话就会过期,SID和RID不再有用!   
  2. 我已经在Servlet上实现了这个逻辑(建立bosh连接和提取sid和rid),这里一旦从Servlet发送响应,Thread将过期并且结束BOSH连接将被终止,所以我是当会话过期时,无法在strophe上执行`Attach()`。

有人可以帮我解决这个问题吗?

2 个答案:

答案 0 :(得分:4)

我相信@ fpsColton的回答是正确的 - 为了清晰起见,我只是添加了额外的信息。根据链接主题的要求,这里是我对此进行的代码更改 - 注意:我只添加了标有" DH"

的部分

在BOSHConnection中:

 // DH: function to preserve current api
public void login(String username, String password, String resource)
        throws XMPPException {
    login(username, password, resource, false);         

}

// DH: Most of this is existing login function, but added prebind parameter 
//     to allow leaving function after all required pre-bind steps done and before 
//     presence stanza gets sent (sent from attach in XMPP client)
public void login(String username, String password, String resource, boolean preBind)         
        throws XMPPException {
    if (!isConnected()) {
        throw new IllegalStateException("Not connected to server.");
    }
    if (authenticated) {
        throw new IllegalStateException("Already logged in to server.");
    }
    // Do partial version of nameprep on the username.
    username = username.toLowerCase().trim();

    String response;
    if (config.isSASLAuthenticationEnabled()
            && saslAuthentication.hasNonAnonymousAuthentication()) {
        // Authenticate using SASL
        if (password != null) {
            response = saslAuthentication.authenticate(username, password, resource);
        } else {
            response = saslAuthentication.authenticate(username, resource, config.getCallbackHandler());
        }
    } else {
        // Authenticate using Non-SASL
        response = new NonSASLAuthentication(this).authenticate(username, password, resource);
    }

    // Indicate that we're now authenticated.
    authenticated = true;
    anonymous = false;

    // DH: Prebind only requires connect and authenticate
    if (preBind) {
        return;
    }

    // Set the user.
    if (response != null) {
        this.user = response;
        // Update the serviceName with the one returned by the server
        config.setServiceName(StringUtils.parseServer(response));
    } else {
        this.user = username + "@" + getServiceName();
        if (resource != null) {
            this.user += "/" + resource;
        }
    }

    // Create the roster if it is not a reconnection.
    if (this.roster == null) {
        this.roster = new Roster(this);
    }
    if (config.isRosterLoadedAtLogin()) {
        this.roster.reload();
    }

    // Set presence to online.
    if (config.isSendPresence()) {
        sendPacket(new Presence(Presence.Type.available));
    }

    // Stores the autentication for future reconnection
    config.setLoginInfo(username, password, resource);

    // If debugging is enabled, change the the debug window title to include
    // the
    // name we are now logged-in as.l
    if (config.isDebuggerEnabled() && debugger != null) {
        debugger.userHasLogged(user);
    }
}

 // DH
@Override
public void disconnect() {
    client.close();
}

然后我的客户端(Web Server)包装类 - 用于从JSP内部进行连接:

注意:这是证明代码而非生产 - 所以这里有一些你可能不想要的东西。

public class SmackBoshConnector {

private String sessionID = null;
private String authID = null;
private Long requestID = 0L;
private String packetID = null;
private boolean connected = false;

public boolean connect(String userName, String password, String host, int port, final String xmppService) {

    boolean success = false;

    try {

        Enumeration<SaslClientFactory> saslFacts = Sasl.getSaslClientFactories();
        if (!saslFacts.hasMoreElements()) {
            System.out.println("Sasl Provider not pre-loaded"); 
            int added = Security.addProvider(new com.sun.security.sasl.Provider()); 
            if (added == -1) {
                System.out.println("Sasl Provider could not be loaded");
                System.exit(added);
            }
            else {
                System.out.println("Sasl Provider added"); 
            }                                                      
        }

        BOSHConfiguration config = new BOSHConfiguration(false, host, port, "/http-bind/", xmppService);
        BOSHConnection connection = new BOSHConnection(config);      

        PacketListener sndListener = new PacketListener() {

            @Override
            public void processPacket(Packet packet) {
                SmackBoshConnector.this.packetID = packet.getPacketID();
                System.out.println("Send PacketId["+packetID+"] to["+packet.toXML()+"]");
            }

        };

        PacketListener rcvListener = new PacketListener() {

            @Override
            public void processPacket(Packet packet) {
                SmackBoshConnector.this.packetID = packet.getPacketID();
                System.out.println("Rcvd PacketId["+packetID+"] to["+packet.toXML()+"]");
            }

        };

        PacketFilter packetFilter = new PacketFilter() {

            @Override
            public boolean accept(Packet packet) {
                return true;
            }
        };

        connection.addPacketSendingListener(sndListener, packetFilter);
        connection.addPacketListener(rcvListener, packetFilter);
        connection.connect();

        // login with pre-bind only
        connection.login(userName, password, "", true);                  

        authID = connection.getConnectionID();

        BOSHClient client = connection.getClient();

        sessionID = client.getSid();
        requestID = client.getRid();

        System.out.println("Connected ["+authID+"] sid["+sessionID+"] rid["+requestID+"]");
        success = true;
        connected = true;

        try {
            Thread.yield();
            Thread.sleep(500);
        }
        catch (InterruptedException e) {
            // Ignore
        }
        finally {
            connection.disconnect();
        }

    } catch (XMPPException ex) {
        Logger.getLogger(SmackBoshConnector.class.getName()).log(Level.SEVERE, null, ex);
    }

    return success;
}

public boolean isConnected() {
    return connected;
}

public String getSessionID() {
    return sessionID;
}

public String getAuthID() {
    return authID;
}

public String getRequestIDAsString() {
    return Long.toString(requestID);
}

public String getNextRequestIDAsString() {
    return Long.toString(requestID+1);
}
public static void main(String[] args)  {        
    SmackBoshConnector bc = new SmackBoshConnector();        
    bc.connect("dazed", "i3ji44mj7k2qt14djct0t5o709", "192.168.2.15", 5280, "my.xmppservice.com");
 }

}

我承认我没有完全记住为什么我把Thread.yield和Thread.sleep(1/2秒)放在这里 - 我想 - 正如你可以看到添加的PacketListener - 较低级别的函数在发送数据之后和从服务器返回响应之前返回 - 如果在服务器发送响应之前断开连接,那么它(也)会使它清理会话并且赢得了许多事情。工作。然而,正如@fpsColton所说,这可能是dicsonnect()实际上并不需要。

编辑:我现在记得更多关于whay我包括sleep()和yield()。我注意到Smack库在几个地方包含sleep(),包括source的XMPPConnection.shutdown()。另外就产量而言()我的环境中存在问题(Oracle数据库中的Java - 可能是非典型的),当它不包括在内时 - 按照Smack Forum Thread

祝你好运。

答案 1 :(得分:2)

使用smack创建了BOSH会话并提取了SID + RID值之后,需要将它们传递给Strophe的attach(),从这里开始你需要让strophe处理这个连接。 Strophe连接后,您不希望服务器对连接做任何事情。

如果您的服务器端代码在连接strophe之后向连接管理器发送任何消息,则很可能它会发送一个无效的RID,这将导致您的会话终止。

同样,一旦会话建立并且可以通过strophe使用,请不要尝试从服务器端继续使用它。在服务器端bosh客户端完成身份验证并且您已将SID + RID传递给页面之后,只需销毁服务器端连接对象,不要尝试断开连接或其他任何操作,因为这将终止您的会话。

您需要记住的是,与通过TCP的传统XMPP连接不同,BOSH客户端不保持与服务器的持久连接(这就是我们在Web应用程序中使用BOSH的原因)。所以没有什么可以断开的。持久连接实际上是在XMPP服务器和BOSH连接管理器之间,这不是您需要处理的事情。因此,当您从服务器端BOSH客户端调用disconnect时,您告诉连接管理器结束会话并关闭它与XMPP服务器的连接,这完全违背了创建会话的目的。