草庐IT

Android XMPP 避免资源冲突

coder 2023-12-27 原文

我正在为我的 XMPP 连接/服务器使用 Smack 和 Openfire。但是我遇到了资源冲突这个非常(显然)常见的问题。谁能告诉我处理冲突的正确方法?

Openfire 设置为始终踢掉原始资源(这是一个定制平台,不向公众开放)。但是我仍然收到错误并且没有获得新的连接。我的 XMPP 类(class)如下。

package com.goosesys.gaggle.services;

import java.util.Collection;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;

import com.google.gson.Gson;
import com.goosesys.gaggle.Globals;
import com.goosesys.gaggle.application.AppSettings;
import com.goosesys.gaggle.application.Utility;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

public class BackgroundXmppConnector extends Service
{
    private ConnectionConfiguration acc;
    private XMPPConnection xConnection;
    private final IBinder mBinder = new XmppBinder();
    private final Handler mHandler = new Handler();

    private final int manualReconnectionTimer = (5 * 60 * 1000);

    private static int mInterval1m = (2 * 60 * 1000);
    private static int mInterval5m = (5 * 60 * 1000);
    private static boolean bConnecting = false;
    private static final Object connectLock = new Object(); 
    private static final Object checkLock = new Object();

    private final Runnable checkConnection = new Runnable()
    {
        @Override
        public void run()
        {
            synchronized(checkLock)
            {
                Log.d("BXC", "Handler running - Checking connection");
                checkConnectionStatus();
            }
        }
    };

    private final Runnable killConnection = new Runnable()
    {
        @Override
        public void run()
        {
            synchronized(checkLock)
            {
                Log.d("BXC", "Killing connection and restarting");

                // Manually disconnect and restart the connection every 5 minutes
                if(xConnection != null)
                    xConnection.disconnect();

                destroyConnectionAndRestart();
                new LoginTask().execute();

                mHandler.postDelayed(this, mInterval5m);
            }
        }
    };

    @Override
    public void onCreate()
    {   
        Log.i("BXC", "BackgroundXmppConnector Service has been created");

        // Checks the connection state every 1 minute //
        mHandler.postDelayed(checkConnection, mInterval1m);
        mHandler.postDelayed(killConnection, mInterval5m);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {   
        Log.d("BXC", "Xmpp Connector Started"); 
        new LoginTask().execute();

        return Service.START_STICKY;
    }

    private void destroyConnectionAndRestart()
    {
        xConnection.disconnect();
        xConnection = null;     
        Globals.backgroundXmppConnectorRunning = false;
        bConnecting = false;
    }

    private void setupConnection()
    {   
        Log.d("BXC", "Settting up XMPP connection");
        try 
        {
            if(!bConnecting && !Globals.backgroundXmppConnectorRunning)
            {
                acc = new ConnectionConfiguration(AppSettings.XMPP_SERVER_HOST,
                        AppSettings.XMPP_SERVER_PORT);
                acc.setSecurityMode(SecurityMode.disabled);
                acc.setSASLAuthenticationEnabled(false);
                acc.setReconnectionAllowed(false);
                acc.setSendPresence(true);

                xConnection = new XMPPConnection(acc);          
                xConnection.addConnectionListener(new ConnectionListener()
                    {
                        @Override
                        public void connectionClosed() 
                        {
                            Log.e("BXC", "Xmpp connection closed");
                            Globals.backgroundXmppConnectorRunning = false;
                            Globals.numberOfDisconnects += 1;
                            //destroyConnectionAndRestart();

                            Utility.writeToLog(getApplicationContext(), "Xmpp Connection closed - disconnected# (" + Globals.numberOfDisconnects + ")");
                        }

                        @Override
                        public void connectionClosedOnError(Exception e) 
                        {
                            Log.e("BXC", "Xmpp connection closed with error: " + e);
                            Globals.backgroundXmppConnectorRunning = false; 
                            Globals.numberOfDisconnectsOnError += 1;                            
                            // This is more than likely due to a conflict loop - it's best to disconnect and nullify
                            // our connection and let the software restart when it checks every 5 minutes
                            if(e.toString().toUpperCase().contains("CONFLICT"))
                            {
                                Log.e("BXC", "Conflict connection loop detected - Waiting");
                            }

                            Utility.writeToLog(getApplicationContext(), "Xmpp Connection closed with error [" + e + "] - disconnected# (" + Globals.numberOfDisconnectsOnError + ")");
                        }

                        @Override
                        public void reconnectingIn(int seconds) 
                        {
                            Log.i("BXC", "Xmpp connection, reconnecting in " + seconds + " seconds");
                            Globals.backgroundXmppConnectorRunning = false;                 
                            bConnecting = true;
                        }

                        @Override
                        public void reconnectionFailed(Exception e) 
                        {
                            Log.e("BXC", "Xmpp reconnection failed: " + e);
                            Globals.backgroundXmppConnectorRunning = false;     
                            //destroyConnectionAndRestart();

                            Utility.writeToLog(getApplicationContext(), "Xmpp reConnection failed with error [" + e + "] - disconnected# (" + Globals.numberOfDisconnects + ")");
                        }

                        @Override
                        public void reconnectionSuccessful() 
                        {
                            Log.i("BXC", "Xmpp reconnected successfully");
                            Globals.backgroundXmppConnectorRunning = true;
                            bConnecting = false;
                        }           
                });
            }
            else
            {
                Log.i("BXC", "Already in connecting state");
            }

        } 
        catch (Exception e)
        {
            Log.e("BXC", e.getMessage());
        }   
    }

    public boolean sendMessage(Intent intent)
    {
        if(xConnection != null && xConnection.isConnected())
        {
            String jsonObject;
            Bundle extras = intent.getExtras();
            if(extras != null)
            {
                jsonObject = extras.getString("MESSAGEDATA");
                Message m = new Gson().fromJson(jsonObject, Message.class);
                if(m != null)
                {
                    sendMessage(m);
                }
                else
                {
                    Log.e("BXC", "Message to send was/is null. Can't send.");
                }

                m = null;
                jsonObject = null;
                extras = null;
            }

            Log.i("BXC", "Sending Xmpp Packet");
            return true;
        }

        return false;
    }

    /*
     * Sends message to xmpp server - message packet in form of
     * 
     * --------------------MESSAGE PACKET-------------------------
     * TO
     * -----------------------
     * FROM
     * -----------------------
     * BODY
     *      TRANSACTION-------------------------------------------
     *          MessageType
     *          --------------------------------------------------
     *          TransactionObject
     */
    private void sendMessage(Message m)
    {
        try
        {
            Log.d("BXC", "Sending transaction message to Xmpp Server");
            xConnection.sendPacket(m);
            //Toast.makeText(getApplicationContext(), "Packet sent to XMPP", Toast.LENGTH_LONG).show();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }


    private void checkConnectionStatus()
    {
        Log.d("BXC", "Checking Xmpp connection status");

        if(xConnection == null || xConnection.isAuthenticated() == false ||
                xConnection.isConnected() == false || xConnection.isSocketClosed() ||
                Globals.backgroundXmppConnectorRunning == false)
        {
            Log.e("BXC", "Connection to server is dead. Retrying");
            Toast.makeText(getApplicationContext(), "Connection dead - retrying", Toast.LENGTH_SHORT).show();
            destroyConnectionAndRestart();
            new LoginTask().execute();
        }
        else
        {
            Log.i("BXC", "Connection appears to be valid");
            Toast.makeText(getApplicationContext(), "Connection valid", Toast.LENGTH_SHORT).show();
        }

    }

    // BINDER ////////////////////////////////////////////////////////////////////////////////
    @Override
    public IBinder onBind(Intent intent) 
    {
        return mBinder;
    }


    // INTERNAL CLASSES //////////////////////////////////////////////////////////////////////
    public class XmppBinder extends Binder
    {
        public BackgroundXmppConnector getService(){
            return BackgroundXmppConnector.this;
        }
    }

    private class LoginTask extends AsyncTask<Void, Void, Void>
    {
        @Override
        protected Void doInBackground(Void... params)
        {
            // First ensure we've got a connection to work with first
            if(Utility.hasActiveInternetConnection(getApplicationContext()) && 
                    ((!bConnecting) || (!Globals.backgroundXmppConnectorRunning)))
            {
                try
                {   
                    //bConnecting = true;
                    Log.d("BXC", "Beginning connection");
                    synchronized(connectLock)
                    {
                        setupConnection();                              
                        xConnection.connect();  
                        Log.i("BXC", "Login credentials: " + Utility.getAndroidID(getApplicationContext()) + " " + AppSettings.XMPP_KEYSTORE_PASSWORD);
                        xConnection.login(Utility.getAndroidID(getApplicationContext()), AppSettings.XMPP_KEYSTORE_PASSWORD);                   

                        xConnection.getChatManager().addChatListener(new ChatManagerListener(){
                            @Override
                            public void chatCreated(final Chat chat, boolean createdLocally)
                            {
                                if(!createdLocally)
                                {
                                    // add chat listener //
                                    chat.addMessageListener(new BackgroundMessageListener(getApplicationContext()));
                                }
                            }

                        });                 

                        Presence p = new Presence(Presence.Type.subscribe);
                        p.setStatus("Out and About");
                        xConnection.sendPacket(p);

                        Roster r = xConnection.getRoster();                 
                        r.setSubscriptionMode(SubscriptionMode.accept_all);
                        r.createEntry(AppSettings.BOT_NAME, "AbleBot", null);
                        r.addRosterListener(new RosterListener(){

                            @Override
                            public void entriesAdded(Collection<String> addresses) 
                            {               
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries Added: " + s);
                                }
                            }

                            @Override
                            public void entriesDeleted(Collection<String> addresses) 
                            {
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries Deleted: " + s);
                                }                           
                            }

                            @Override
                            public void entriesUpdated(Collection<String> addresses) 
                            {   
                                for(String s : addresses)
                                {
                                    Log.d("BXC", "Entries updated: " + s);
                                }                           
                            }

                            @Override
                            public void presenceChanged(Presence presence) 
                            {   
                                Log.d("BXC", "PresenceChanged: " + presence.getFrom());
                            }                       
                        });
                    }           
                }
                catch(IllegalStateException ex)
                {
                    Log.e("BXC", "IllegalStateException -->");
                    if(ex.getMessage().contains("Already logged in to server"))
                    {
                        Globals.backgroundXmppConnectorRunning = true;                      
                    }
                    else
                    {
                        Globals.backgroundXmppConnectorRunning = false;
                        Utility.writeExceptionToLog(getApplicationContext(), ex);

                        ex.printStackTrace();
                    }
                }
                catch(XMPPException ex)
                {
                    Log.e("BXC", "XMPPException -->");
                    Globals.backgroundXmppConnectorRunning = false;
                    Utility.writeExceptionToLog(getApplicationContext(), ex);
                    ex.printStackTrace();
                }
                catch(NullPointerException ex)
                {
                    Log.e("BXC", "NullPointerException -->");
                    Globals.backgroundXmppConnectorRunning = false;
                    Utility.writeExceptionToLog(getApplicationContext(), ex);
                    ex.printStackTrace();
                }
                catch(Exception ex)
                {
                    Log.e("BXC", "Exception -->");
                    Globals.backgroundXmppConnectorRunning = false;
                    Utility.writeToLog(getApplicationContext(), ex.toString());
                    ex.printStackTrace();
                }

                return null;    
            }
            else
            {
                Log.i("BXC", "No active internet data connection - will retry");
            }

            return null;
        }

        @Override
        protected void onPostExecute(Void ignored)
        {
            if(xConnection != null)
            {
                if(xConnection.isConnected() && (!xConnection.isSocketClosed()))
                {
                    Log.i("BXC", "Logged in to XMPP Server");
                    Globals.backgroundXmppConnectorRunning = true;

                    mHandler.postDelayed(checkConnection, mInterval1m);
                }
                else
                {
                    Log.e("BXC", "Unable to log into XMPP Server.");    
                    Globals.backgroundXmppConnectorRunning = false;

                    destroyConnectionAndRestart();
                }
            }
            else
            {
                Log.e("BXC", "Xmpp Connection object is null");
                Globals.backgroundXmppConnectorRunning = false;
            }
        }
    }

}

据我所知,openfire(当设置为始终启动时)将始终启动原始资源,然后允许新登录连接,但我根本看不到这一点。任何帮助是极大的赞赏。谢谢。

最佳答案

处理资源冲突的正确方法是除非万不得已,否则不要使用固定资源。如果您的客户端在登录时未指定任何资源,则服务器应为其分配一个随机生成的永远不会冲突的资源。

您需要指定固定资源的原因很少,因为大多数客户端无论如何都会隐藏您的联系人资源,还有其他原因可以说明为什么在每个连接上都有一个新资源是有利的(比如避免群聊不同步的常见错误,因为群聊的服务器没有意识到连接用户实际上是一个新 session )。

固定资源的一个大问题是重新连接循环,如果服务器配置为踢掉旧的冲突资源,两个客户端会反复踢对方。您应该确保在收到资源冲突错误时不会自动重新连接。

关于Android XMPP 避免资源冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23605562/

有关Android XMPP 避免资源冲突的更多相关文章

  1. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  2. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  3. ruby-on-rails - Rails 3,嵌套资源,没有路由匹配 [PUT] - 2

    我真的为这个而疯狂。我一直在搜索答案并尝试我找到的所有内容,包括相关问题和stackoverflow上的答案,但仍然无法正常工作。我正在使用嵌套资源,但无法使表单正常工作。我总是遇到错误,例如没有路线匹配[PUT]"/galleries/1/photos"表格在这里:/galleries/1/photos/1/edit路线.rbresources:galleriesdoresources:photosendresources:galleriesresources:photos照片Controller.rbdefnew@gallery=Gallery.find(params[:galle

  4. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  5. ruby - Chef LW 资源属性默认值如何引用另一个属性? - 2

    我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我

  6. ruby - mixin方法名冲突时如何选择调用方法? - 2

    当你在类中包含方法名冲突的模块时,它会使用类定义的方法。有没有办法选择我想运行的?moduleBdefself.hello"helloB"endendclassAincludeBdefself.hello"helloA"endendA.hello#=>thisprints"helloA",whatifIwant"helloB"? 最佳答案 Ben,当你在Ruby中调用一个方法(比如hello)时,会发生以下情况:如果接收者的特征类有一个名为hello的方法,它将被调用。如果不是:如果接收者的类有一个名为hello的实例方法,它将被调

  7. ruby - 无法激活 susy-2.1.1,因为 sass-3.2.17 与 sass 冲突 (~> 3.3.0) - 2

    我已经安装了最新版本的compass、sass和susy。但我仍然收到此错误:Unabletoactivatesusy-2.1.1,becausesass-3.2.17conflictswithsass(~>3.3.0)有人知道这个Ruby是如何工作的吗?这是我安装的gem的列表:***LOCALGEMS***CFPropertyList(2.2.0)chunky_png(1.3.0)compass(0.12.4)compass-core(1.0.0.alpha.19)compass-import-once(1.0.4)compass-rails(1.1.3)fssm(0.2.10)l

  8. ruby - 在 Ruby 中为变量赋值时如何避免控制台输出 - 2

    赋值时是否可以避免这种影响:irb(main):584:0>a=true=>trueirb(main):584:0>我有一个代码有很多赋值,当我试图测试它时,由于所有这些返回值,我看不到结果:truefalsetruefalsetruetrue.. 最佳答案 您可以启动irb或附加--noecho选项的控制台。$irb--noecho2.0.0p353:001>true2.0.0p353:002>否则,如果控制台由另一个进程启动,只需设置conf.echo=false$irb2.0.0p353:001>true=>true2.0.0

  9. ruby - block 看不到方法( Chef 资源) - 2

    假设我们有两个资源:template'template1'doowner'root'group'root'endtemplate'template2'doowner'root'group'root'end我想在资源中重用代码。但是,如果我在配方中定义了一个过程,您会得到owner、group等的NoMethodError。为什么会这样?词法范围没有什么不同,是吗?因此,我必须使用self.instance_eval&common_cfg。common_cfg=Proc.new{owner'root'group'root'}template'template1'docommon_cfg.

  10. ruby - IO::EAGAINWaitReadable:资源暂时不可用 - 读取会阻塞 - 2

    当我尝试使用“套接字”库中的方法“read_nonblock”时出现以下错误IO::EAGAINWaitReadable:Resourcetemporarilyunavailable-readwouldblock但是当我通过终端上的IRB尝试时它工作正常如何让它读取缓冲区? 最佳答案 IgetthefollowingerrorwhenItrytousethemethod"read_nonblock"fromthe"socket"library当缓冲区中的数据未准备好时,这是预期的行为。由于异常IO::EAGAINWaitReadab

随机推荐