Praktik terbaik untuk menyambungkan kembali klien SignalR 2.0 .NET ke hub server

86

Saya menggunakan SignalR 2.0 dengan klien .NET dalam aplikasi seluler yang perlu menangani berbagai jenis pemutusan hubungan. Terkadang klien SignalR terhubung kembali secara otomatis - dan terkadang harus dihubungkan kembali secara langsung dengan menelepon HubConnection.Start()lagi.

Karena SignalR secara ajaib menyambung kembali secara otomatis beberapa waktu, saya bertanya-tanya apakah saya kehilangan fitur atau pengaturan konfigurasi?

Apa cara terbaik untuk menyiapkan klien yang tersambung kembali secara otomatis?


Saya telah melihat contoh javascript yang menangani Closed()acara tersebut dan kemudian Hubungkan setelah n-detik. Apakah ada pendekatan yang direkomendasikan?

Saya telah membaca dokumentasi dan beberapa artikel tentang masa pakai koneksi SignalR, tetapi saya masih belum jelas tentang cara menangani koneksi ulang klien.

Ender2050
sumber

Jawaban:

71

Saya akhirnya menemukan ini. Inilah yang saya pelajari sejak memulai pertanyaan ini:

Latar belakang: Kami sedang membangun aplikasi iOS menggunakan Xamarin / Monotouch dan klien .NET SignalR 2.0.3. Kami menggunakan protokol SignalR default - dan tampaknya menggunakan SSE, bukan soket web. Saya belum yakin apakah mungkin menggunakan soket web dengan Xamarin / Monotouch. Semuanya dihosting menggunakan situs web Azure.

Kami membutuhkan aplikasi untuk terhubung kembali ke server SignalR kami dengan cepat, tetapi kami terus mengalami masalah di mana koneksi tidak terhubung kembali dengan sendirinya - atau menghubungkan kembali membutuhkan waktu tepat 30 detik (karena waktu tunggu protokol yang mendasarinya).

Ada tiga skenario yang akhirnya kami uji:

Skenario A - menghubungkan pertama kali aplikasi dimuat. Ini bekerja dengan sempurna sejak hari pertama. Koneksi selesai dalam waktu kurang dari 0,25 detik bahkan melalui koneksi seluler 3G. (dengan asumsi radio sudah menyala)

Skenario B - menyambungkan kembali ke server SignalR setelah aplikasi menganggur / ditutup selama 30 detik. Dalam skenario ini, klien SignalR pada akhirnya akan menyambung kembali ke server sendiri tanpa pekerjaan khusus - tetapi tampaknya menunggu tepat 30 detik sebelum mencoba menyambung kembali. (terlalu lambat untuk aplikasi kita)

Selama masa tunggu 30 detik ini, kami mencoba memanggil HubConnection.Start () yang tidak berpengaruh. Dan memanggil HubConnection.Stop () juga membutuhkan waktu 30 detik. Saya menemukan bug terkait di situs SignalR yang tampaknya telah diselesaikan , tetapi kami masih mengalami masalah yang sama di v2.0.3.

Skenario C - menghubungkan kembali ke server SignalR setelah aplikasi menganggur / ditutup selama 120 detik atau lebih. Dalam skenario ini, protokol transport SignalR telah habis waktunya sehingga klien tidak pernah terhubung kembali secara otomatis. Ini menjelaskan mengapa klien terkadang tetapi tidak selalu menyambung kembali sendiri. Kabar baiknya adalah, memanggil HubConnection.Start () berfungsi hampir secara instan seperti skenario A.

Jadi, perlu beberapa saat bagi saya untuk menyadari bahwa kondisi penyambungan kembali berbeda berdasarkan apakah aplikasi ditutup selama 30 detik vs 120+ detik. Dan meskipun log pelacakan SignalR menerangi apa yang terjadi dengan protokol yang mendasarinya, saya tidak percaya ada cara untuk menangani peristiwa tingkat pengangkutan dalam kode. (Peristiwa Closed () menyala setelah 30 detik dalam skenario B, langsung dalam skenario C; properti Negara menyatakan "Tersambung" selama periode tunggu penghubung kembali ini; tidak ada peristiwa atau metode relevan lainnya)

Solusi: Solusinya jelas. Kami tidak menunggu SignalR melakukan keajaiban penyambungan kembali. Sebaliknya, ketika aplikasi diaktifkan atau ketika koneksi jaringan ponsel dipulihkan, kami hanya membersihkan acara dan membatalkan referensi HubConnection (tidak dapat membuangnya karena membutuhkan waktu 30 detik, semoga pengumpulan sampah akan menanganinya ) dan membuat instance baru. Sekarang semuanya bekerja dengan baik. Untuk beberapa alasan, saya pikir kita harus menggunakan kembali koneksi yang ada dan menghubungkan kembali daripada hanya membuat contoh baru.

Ender2050
sumber
5
Apakah Anda bersedia memposting beberapa kode? Hanya ingin tahu bagaimana Anda menyusunnya. Saya menggunakan Signalr di aplikasi obrolan dari dalam PCL di aplikasi Xamarin juga. Ini berfungsi sangat baik kecuali saya tidak bisa mengaktifkan sihir koneksi ulang setelah telepon dimatikan dan dihidupkan lagi. Aku bersumpah, IT Crowd mengatakan hanya itu yang harus aku lakukan.
Timothy Lee Russell
1
Hai Ender2050, saya perhatikan bahwa setelah perangkat android terputus dari server maka tidak pernah menyambung kembali lagi. Jadi saya telah menerapkan alarm yang berjalan setiap 5 menit dan memeriksa koneksi signalR dengan hub server. Pada acara centang Alarm saya telah memeriksa apakah objek koneksi null atau connectionId kosong lalu buat koneksi lagi Tapi ini tidak berfungsi dengan baik. Pengguna perlu mematikan aplikasi dan membukanya kembali. Saya telah menggunakan java-client untuk android dan C # .Net untuk serve hub. Mencari bantuan Anda untuk mengatasi masalah ini.
jignesh
1
FYI, Mono tidak memiliki soket web. Inilah mengapa aplikasi Xamarin Anda selalu menggunakan SSE. Anda dapat menulis klien konsol. Jika Anda menjalankannya di Mono, itu akan menggunakan SSE. Jika Anda menjalankannya di Windows (setidaknya Windows 8 karena 7 juga tidak mendukung soket web) itu akan menggunakan soket web.
daramasala
@ Ender2050 Harap dapat Anda memperluas solusi Anda dengan beberapa contoh kode?
mbx-mbx
Kami mengalami masalah koneksi ulang dengan SignalR Hub (pustaka SignalR versi 2.2.2) dari Aplikasi Android yang menggunakan "Pustaka Klien Java SignalR" dan Aplikasi iOS yang menggunakan "pustaka C SignalR Objek". Pustaka klien di kedua platform belum diperbarui dalam beberapa saat. Saya pikir masalahnya adalah karena ketidakcocokan protokol SignalR antara klien dan server.
Nadim Hossain Sonet
44

Menyetel pengatur waktu pada peristiwa terputus untuk secara otomatis mencoba menyambungkan kembali adalah satu-satunya metode yang saya ketahui.

Dalam javascript dilakukan seperti ini:

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

Ini adalah pendekatan yang direkomendasikan dalam dokumentasi:

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

KingOfHypocrites
sumber
1
satu petunjuk - pastikan Anda melakukan fungsionalitas awal yang lengkap di awal seperti yang Anda lakukan, misalnya menghubungkan kembali ke hub.
MikeBaz - MSFT
1
Saya menemukan bahwa dengan klien .NET, jika Anda berlangganan ke acara Tertutup sebelum Anda memanggil hub. Start (), jika ada kegagalan untuk menyambung pada awalnya, maka penangan acara Tertutup Anda dipanggil dan mencoba memanggil hub. Mulai () lagi , menyebabkan hub asli. Start () tidak pernah selesai. Solusi saya adalah hanya berlangganan Closed setelah Start () berhasil, dan berhenti berlangganan Closed segera di panggilan balik.
Oran Dennison
3
@MikeBaz Saya pikir maksud Anda menyambung kembali ke grup
Simon_Weaver
1
@KingOfHypocrites Saya berlangganan reconnectingacara, yang dijalankan setelah hub kehilangan koneksi dan mengatur variabel itu (misalnya shouldReconnect) ke true. Jadi saya mengadaptasi contoh Anda untuk memeriksa variabel itu. Ini terlihat bagus.
Alisson
2
Saya melakukan nomor acak antara 10 dan 60 detik. Kami memiliki terlalu banyak klien untuk hanya menempatkan 5 detik, kami akan melakukan DDoS sendiri. $ .connection.hub.disconnected (function () {setTimeout (function () {$ .connection.hub.start ();}, (Math.floor (Math.random () * 50) + 10) * 1000); });
Brain2000
17

Sejak OP meminta klien .NET (implementasi winform di bawah),

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")
    }
}
ibubi
sumber
Saya menemukan di SignalR 2.3.0 bahwa jika saya menunggu untuk terhubung dalam acara Closed () kadang-kadang tidak dapat terhubung. Namun, jika saya memanggil Tunggu () manual pada acara dengan waktu tunggu, seperti 10 detik, itu akan secara otomatis memanggil Closed () lagi dan lagi setiap 10 detik, dan kemudian menghubungkan kembali akan bekerja.
Brain2000
0

Saya menambahkan beberapa pembaruan untuk jawaban ibubi . Mungkin seseorang membutuhkannya. Saya menemukan bahwa dalam beberapa kasus signalr tidak naik acara "tertutup" setelah menghubungkan kembali dihentikan. Saya menyelesaikannya menggunakan acara "StateChanged". Metode yang terhubung ke server SignalR:

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;
        }

Metode untuk menghubungkan kembali:

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

Metode upaya tanpa henti untuk terhubung ke server (Juga saya menggunakan metode ini untuk membuat koneksi tinju):

public async Task RestartConnection()
        {
            while (!ApplicationClosed)
            {
                bool connected = await ConnectToSignalRServer();
                if (connected)
                    return;
            }
        }
Константин Золин
sumber
-3

Anda dapat mencoba memanggil metode server dari android Anda sebelum status koneksi ulang mulai untuk mencegah masalah koneksi ulang sihir.

SignalR Hub C #

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

Di Android

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);
    });
}
Jongz Puangput
sumber