SignalR 2.0 .NET istemcisini sunucu hub'ına yeniden bağlamak için en iyi uygulama


86

Bir mobil uygulamada .NET istemcisi ile SignalR 2.0 kullanıyorum ve çeşitli bağlantı kesme türlerini ele alması gerekiyor. Bazen SignalR istemcisi otomatik olarak yeniden bağlanır - ve bazen HubConnection.Start()tekrar arayarak doğrudan yeniden bağlanması gerekir .

SignalR bazen sihirli bir şekilde otomatik olarak yeniden bağlandığından, bir özelliği mi yoksa yapılandırma ayarını mı kaçırdığımı merak ediyorum.

Otomatik olarak yeniden bağlanan bir istemci kurmanın en iyi yolu nedir?


Closed()Olayı işleyen javascript örnekleri gördüm ve ardından n saniye sonra Bağlan. Önerilen herhangi bir yaklaşım var mı?

Okuduğum belgelerine ve SignalR bağlantılarının ömrü hakkında çeşitli makaleler, ama yine de istemci yeniden bağlanma nasıl ele alınacağı üzerinde anlamış değilim.

Yanıtlar:


71

Sonunda bunu çözdüm. İşte bu soruya başladığımdan beri öğrendiklerim:

Arka plan: Xamarin / Monotouch ve .NET SignalR 2.0.3 istemcisini kullanarak bir iOS uygulaması oluşturuyoruz. Varsayılan SignalR protokollerini kullanıyoruz - ve web soketleri yerine SSE kullanıyor gibi görünüyor. Xamarin / Monotouch ile web soketlerini kullanmanın mümkün olup olmadığından henüz emin değilim. Her şey Azure web siteleri kullanılarak barındırılır.

Uygulamanın SignalR sunucumuza hızlı bir şekilde yeniden bağlanmasına ihtiyacımız vardı, ancak bağlantının kendi kendine yeniden bağlanmadığı veya yeniden bağlanmanın tam olarak 30 saniye sürdüğü (temel protokol zaman aşımından dolayı) sorunlar yaşamaya devam ettik.

Test ettiğimiz üç senaryo vardı:

Senaryo A - uygulama ilk yüklendiğinde bağlanma. Bu, ilk günden itibaren kusursuz bir şekilde çalıştı. Bağlantı, 3G mobil bağlantılarda bile .25 saniyeden daha kısa sürede tamamlanır. (radyonun zaten açık olduğunu varsayarak)

Senaryo B - uygulama 30 saniye boşta kaldıktan / kapatıldıktan sonra SignalR sunucusuna yeniden bağlanma. Bu senaryoda, SignalR istemcisi sonunda herhangi bir özel çalışma olmaksızın sunucuya kendi başına yeniden bağlanacaktır - ancak yeniden bağlanmayı denemeden önce tam olarak 30 saniye beklediği görülmektedir. (uygulamamız için çok yavaş)

Bu 30 saniyelik bekleme süresi boyunca, etkisi olmayan HubConnection.Start () 'ı çağırmayı denedik. HubConnection.Stop () 'u çağırmak da 30 saniye sürer. SignalR sitesinde çözülmüş gibi görünen ilgili bir hata buldum , ancak v2.0.3'te hala aynı sorunu yaşıyoruz.

Senaryo C - uygulama 120 saniye veya daha uzun süre boşta kaldıktan / kapatıldıktan sonra SignalR sunucusuna yeniden bağlanma. Bu senaryoda, SignalR aktarım protokolü zaten zaman aşımına uğradı, bu nedenle istemci asla otomatik olarak yeniden bağlanmaz. Bu, müşterinin neden bazen ancak her zaman kendi kendine yeniden bağlantı kurduğunu açıklar. İyi haber, HubConnection.Start () 'ı çağırmak neredeyse anında senaryo A gibi çalışır.

Bu nedenle, uygulamanın 30 saniyeye karşı 120 saniyeden fazla kapalı olmasına bağlı olarak yeniden bağlanma koşullarının farklı olduğunu anlamam biraz zaman aldı. Ve SignalR izleme günlükleri temeldeki protokolde neler olup bittiğini aydınlatsa da, koddaki taşıma düzeyi olaylarını ele almanın bir yolu olduğuna inanmıyorum. (Closed () olayı, senaryo B'de 30 saniye sonra, C senaryosunda anında tetiklenir; State özelliği, bu yeniden bağlanma bekleme süreleri sırasında "Bağlı" der; başka ilgili olay veya yöntem yoktur)

Çözüm: Çözüm ortada. SignalR'ın yeniden bağlanma sihrini yapmasını beklemiyoruz. Bunun yerine, uygulama etkinleştirildiğinde veya telefonun ağ bağlantısı geri yüklendiğinde, yalnızca olayları temizliyoruz ve HubConnection referansını kaldırıyoruz (30 saniye sürdüğü için onu atamıyoruz, umarım çöp toplama işi halleder. ) ve yeni bir örnek oluşturma. Şimdi her şey harika çalışıyor. Bazı nedenlerden dolayı, sadece yeni bir örnek oluşturmak yerine kalıcı bir bağlantıyı yeniden kullanmamız ve yeniden bağlanmamız gerektiğini düşündüm.


5
Biraz kod göndermek ister misiniz? Sadece nasıl yapılandırdığını merak ediyorum. Bir Xamarin uygulamasındaki bir PCL içinden bir sohbet uygulamasında Signalr kullanıyorum. Telefon kapanıp tekrar açıldıktan sonra yeniden bağlanma sihrini elde edememem dışında gerçekten harika çalışıyor. Yemin ederim IT Crowd yapmam gereken tek şeyin bu olduğunu söyledi.
Timothy Lee Russell

1
Merhaba Ender2050, android cihazın sunucudan bağlantısı kesildikten sonra bir daha asla yeniden bağlanmadığını fark ettim. Bu yüzden her 5 dakikada bir çalışan ve sunucu hub ile sinyalR bağlantısını kontrol eden alarmı uyguladım. Alarm onay olayında bağlantı nesnesinin boş olup olmadığını kontrol ettim veya connectionId boş ve ardından bağlantıyı tekrar kurdu ancak bu iyi çalışmıyor. Kullanıcının uygulamayı kapatması ve yeniden açması gerekiyor Android için java-client ve hizmet merkezi için C # .Net kullandım. Bu sorunu çözmek için yardımınızı arıyorum.
jignesh

1
Bilginize, Mono'nun web soketleri yok. Xamarin uygulamalarınızın her zaman SSE kullanmasının nedeni budur. Bir konsol istemcisi yazabilirsiniz. Mono'da çalıştırırsanız, SSE kullanacaktır. Windows'ta çalıştırırsanız (en azından Windows 8, çünkü 7 web soketlerini de desteklemiyor) web soketlerini kullanacaktır.
daramasala

@ Ender2050 Lütfen çözümünüzü bazı kod örnekleri ile genişletebilir misiniz?
mbx-mbx

"SignalR Java İstemci kitaplığı" kullanan Android Uygulamasından ve "SignalR Nesne C kitaplığı" kullanan iOS Uygulamasından SignalR Hub (SignalR kitaplığı sürüm 2.2.2) ile yeniden bağlantı sorunu yaşıyoruz. Her iki platformdaki istemci kitaplıkları bir süredir güncellenmedi. Sanırım sorun istemci ve sunucu arasındaki SignalR protokolü uyumsuzluğundan kaynaklanıyor.
Nadim Hossain Sonet

44

Bildiğim tek yöntem, bağlantısız olayda otomatik olarak yeniden bağlanmayı denemek için bir zamanlayıcı ayarlamak.

JavaScript'te şu şekilde yapılır:

$.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});

Dokümantasyonda önerilen yaklaşım budur:

http://www.asp.net/signalr/overview/signalr-20/hubs-api/handling-connection-lifetime-events#clientdisconnect


1
bir ipucu - aksi halde hub'lara yeniden bağlanacağınız için, başlangıçta tam bir başlangıç ​​işlevi yaptığınızdan emin olun.
MikeBaz - MSFT

1
.NET istemcisiyle, hub.Start () 'ı çağırmadan önce Closed olayına abone olursanız, başlangıçta bağlanmada bir başarısızlık varsa, Kapalı olay işleyiciniz çağrılır ve hub'ı yeniden çağırmaya çalışır. , orijinal hub.Start () 'ın hiçbir zaman tamamlanmamasına neden olur. Çözümüm, yalnızca Başlat () başarılı olduktan sonra Kapalı'ya abone olmak ve geri aramada hemen Kapalı aboneliğinden çıkmaktı.
Oran Dennison

3
@MikeBaz Ben gruplarına size ortalama yeniden bağlanma düşünüyorum
Simon_Weaver

1
@KingOfHypocrites reconnectingHub bağlantısını kaybettiğinde ve bu değişkeni (örneğin shouldReconnect) true olarak ayarladığında tetiklenen olaya abone oldum . Ben de bu değişkeni kontrol etmek için örneğinizi uyarladım. Güzel görünüyor.
Alisson

2
10 ila 60 saniye arasında rastgele bir sayı yaptım. 5 saniye ayırmak için çok fazla müşterimiz var, biz kendimiz DDoS yapardık. $ .connection.hub.disconnected (function () {setTimeout (function () {$ .connection.hub.start ();}, (Math.floor (Math.random () * 50) + 10) * 1000); });
Brain2000

17

OP bir .NET istemcisi istediğinden (aşağıdaki bir winform uygulaması),

private async Task<bool> ConnectToSignalRServer()
{
    bool connected = false;
    try
    {
        Connection = new HubConnection("server url");
        Hub = Connection.CreateHubProxy("MyHub");
        await Connection.Start();

        //See @Oran Dennison's comment on @KingOfHypocrites's answer
        if (Connection.State == ConnectionState.Connected)
        {
            connected = true;
            Connection.Closed += Connection_Closed;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
    return connected;
}

private async void Connection_Closed()
{   // A global variable being set in "Form_closing" event 
    // of Form, check if form not closed explicitly to prevent a possible deadlock.
    if(!IsFormClosed) 
    {
        // specify a retry duration
        TimeSpan retryDuration = TimeSpan.FromSeconds(30);
        DateTime retryTill = DateTime.UtcNow.Add(retryDuration);

        while (DateTime.UtcNow < retryTill)
        {
            bool connected = await ConnectToSignalRServer();
            if (connected)
                return;
        }
        Console.WriteLine("Connection closed")
    }
}

SignalR 2.3.0'da, Closed () olayında bağlanmayı bekleseydim, bazen bağlanmayacağını buldum. Ancak, olayda 10 saniye gibi bir zaman aşımına sahip manuel bir Wait () çağırırsam, her 10 saniyede bir otomatik olarak Closed () öğesini tekrar ve tekrar çağırır ve ardından yeniden bağlantı çalışır.
Brain2000

0

İbubi cevabı için bazı güncellemeler ekledim . Birinin buna ihtiyacı olabilir. Bazı durumlarda, tekrar bağlantı durdurulduktan sonra sinyalin "kapalı" olayı yükselmediğini buldum. "StateChanged" olayını kullanarak çözdüm. SignalR sunucusuna bağlanan yöntem:

private async Task<bool> ConnectToSignalRServer()
        {
            bool connected = false;
            try
            {
                var connection = new HubConnection(ConnectionUrl);
                var proxy = connection.CreateHubProxy("CurrentData");
                await connection.Start();

                if (connection.State == ConnectionState.Connected)
                {
                    await proxy.Invoke("ConnectStation");

                    connection.Error += (ex) =>
                    {
                        Console.WriteLine("Connection error: " + ex.ToString());
                    };
                    connection.Closed += () =>
                    {
                        Console.WriteLine("Connection closed");
                    };
                    connection.StateChanged += Connection_StateChanged;
                    Console.WriteLine("Server for Current is started.");
                    connected = true;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
            return connected;
        }

Yeniden bağlanma yöntemi:

private async void Connection_StateChanged(StateChange obj)
        {
            if (obj.NewState == ConnectionState.Disconnected)
            {
                await RestartConnection();
            }
        }

Sunucuya sonsuz bağlanma girişimleri yöntemi (Ayrıca ilk bağlantı oluşturmak için bu yöntemi kullanıyorum):

public async Task RestartConnection()
        {
            while (!ApplicationClosed)
            {
                bool connected = await ConnectToSignalRServer();
                if (connected)
                    return;
            }
        }

-3

Sihirli yeniden bağlanma sorununu önlemek için yeniden bağlanma durumu başlamadan önce androidinizden sunucu yöntemini çağırmayı deneyebilirsiniz.

SignalR Hub C #

 public class MyHub : Hub
    {
        public void Ping()
        {
            //ping for android long polling
        }
 }

Android'de

private final int PING_INTERVAL = 10 * 1000;

private boolean isConnected = false;
private HubConnection connection;
private ClientTransport transport;
private HubProxy hubProxy;

private Handler handler = new Handler();
private Runnable ping = new Runnable() {
    @Override
    public void run() {
        if (isConnected) {
            hubProxy.invoke("ping");
            handler.postDelayed(ping, PING_INTERVAL);
        }
    }
};

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    System.setProperty("http.keepAlive", "false");

    .....
    .....

    connection.connected(new Runnable() {
        @Override
        public void run() {
            System.out.println("Connected");
            handler.postDelayed(ping, PING_INTERVAL);
    });
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.