Come posso interrompere una connessione SignalR?


10

Sto utilizzando SignalR per trasferire dati su un sito Web. Ma SignalR dovrebbe essere in grado di inviare dati solo per un periodo di tempo e se il periodo di tempo è trascorso, la connessione dovrebbe essere interrotta.

La funzione di arresto $.connection.hub.stop()viene annullata se una richiesta è ancora in sospeso e non è stata completata. Ma questa richiesta dovrebbe essere forzata all'annullamento, indipendentemente dalla quantità di dati inviati.

Come posso uccidere una connessione SignalR?

Risposte:


6

Come puoi vedere in questa documentazione Microsoft sul timeout e le impostazioni keepalive, puoi definire DisconnectTimeout nelle opzioni.

Esempio:

protected void Application_Start(object sender, EventArgs e)
{
    // Make long-polling connections wait a maximum of 110 seconds for a
    // response. When that time expires, trigger a timeout command and
    // make the client reconnect.
    GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);

    // Wait a maximum of 30 seconds after a transport connection is lost
    // before raising the Disconnected event to terminate the SignalR connection.
    GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);

    // For transports other than long polling, send a keepalive packet every
    // 10 seconds. 
    // This value must be no more than 1/3 of the DisconnectTimeout value.
    GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);

    RouteTable.Routes.MapHubs();
}

Modifica : dal momento che vuoi interrompere la connessione dal client, non importa cosa, stai parlando di un CancellationTokencomportamento, ma sfortunatamente questo non è ancora supportato in SignalR come puoi vedere qui e qui , il team vuole farlo SignalRma ancora c'è nessuna notizia al riguardo.


Come ho affermato, la richiesta dal sito di frontend non è stata completata, quindi ci sono ancora alcuni dati dal frontend da inviare a SignalR-Backend / Hub. Quindi sto cercando una soluzione di frontend perché viene inviata una discreta quantità di dati e se è trascorso un periodo di tempo, la connessione dovrebbe essere interrotta dal frontend indipendentemente dal fatto che i dati siano stati trasmessi o meno. Capisci cosa sto cercando?
Snickbrack

@Snickbrack vuoi interrompere la connessione dal lato client, anche se stai inviando dati in questo momento, ho ragione?
Kiril1512

1
sì. Hai ragione.
Snickbrack

@Snickbrack ha aggiornato la mia risposta.
Kiril1512

@Snickbrack non dimenticare di selezionare la risposta corretta alla tua domanda, questa o altre risposte qui ...
Kiril1512,

1

Leggere questo documento di Microsoft sull'evento di durata dell'hub. È possibile modificare i valori predefiniti per queste impostazioni, impostarli Application_Startnel proprio Global.asaxfile. Ma in questo modo non puoi controllare completamente il lato client. Quindi usi la setTimeoutfunzione javascript e passi la fine del modulo del server quando può essere un nuovo utente connect.it GlobalHost.Configuration.DisconnectTimeouto quando vuoi. Faccio un esempio completo con il progetto demo. In realtà uso questa logica in un sistema di biglietteria molto grande per il biglietto di detenzione in tempo reale. (leggi tutti i commenti in linea)

Modello:

public class MyModel
{
    public int Id { get; set; }

    public string Name { get; set; }


    public static string Send(MyModel my)
    {
        //Do Somthing           
        return $"Data Sending to {my.Name}...";
    }
    public static string Stop(string name)
    {
        //Do Somthing

        return $"ForceStop {name}.";
    }
    public static string Delete()
    {
        //Do Somthing

        return "Deleted";
    }
}

Centro:

[HubName("myHub")]
public class MyHub : Hub
{
    int connectionTimeOut = 10;//sec

    [HubMethodName("connect")]
    public void Connect()
    {  
            //apply logic if any when user connected or reload page
            //set connection Time Out as you need
        connectionTimeOut= 10;// GlobalHost.Configuration.DisconnectTimeout

       Clients.Client(Context.ConnectionId).onNewUserConnected(connectionTimeOut);
    }
    [HubMethodName("startSendingServer")]
    public void StartSending(int id, string name)//pass anything you need
    {
        //apply logic if any when start sending data

        var my = new MyModel
        {
            Id = id,
            Name = name
        };
        var status = MyModel.Send(my);//example

        var result = new
        {
            status,
            name
        };

        Clients.Client(Context.ConnectionId).startSendingClient(result);

    }

    [HubMethodName("forceStopServer")]
    public void ForceStop(string name)//pass anything you need
    {
        //apply logic if any when force stop sending data
        var status = MyModel.Stop(name);
        Clients.Client(Context.ConnectionId).forceStopClint(status);
    }


    public override Task OnDisconnected(bool stopCalled)
    {

        //apply logic if any when connection Disconnected

        var status = MyModel.Delete();//example
        if (stopCalled)
        {
            //  status=String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId)
            //your code here
        }
        else
        {
            // status=String.Format("Client {0} timed out .", Context.ConnectionId);
            //your code here
            //Clients.Client(Context.ConnectionId).onUserDisconnected(status);
        }

        return base.OnDisconnected(stopCalled);
    }


}

TestView:

<div class="row">
    <div class="col-md-12">
        <h1> Status: <span id="hubStatus"></span></h1>
        <br />
        <h4> Countdown : <span id="counter"></span></h4>
        <br />

        <button id="btnHub" class="btn btn-primary btn-lg">Start Sending Data</button>
    </div>
</div>
@section scripts{
    <script src="~/Scripts/app/hub.js"></script>
}

hub.js:

var proxyTimer = null;
var sendTimeLimit = 1;//sec
var sessionTime = sendTimeLimit * 1000;

$(function () {
    var myProxy = $.connection.myHub;
    $.connection.hub.start().done(function () {
        registerServerEvents(myProxy);
    });

    clientMethods(myProxy);
});

function registerServerEvents(proxyHub) {
    proxyHub.server.connect();
    $(document).on("click", "#btnHub", function (e) {

        $("#hubStatus").html("Sending..");
        $("#btnHub").text("Count Down Start...");

        //Logic Before start sending data.
        var id = 1;
        var name = "AzR";        
        proxyHub.server.startSendingServer(id,name);

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

        $.connection.hub.disconnected(function () {
            $("#hubStatus").html("Disconnected");// you can restart on here.     
            $("#btnHub").text("Stat Again after reload window");

        });

    });
}



function clientMethods(proxyHub) {

    //proxyHub.on('onConnected', function (sendTimeLimit) {
    //    sendTimeLimit = sendTimeLimit;
    //});

    proxyHub.on('onNewUserConnected', function (serverItem) {
        sendTimeLimit = serverItem;
        sessionTime = sendTimeLimit * 1000;
    });


    proxyHub.on('startSendingClient', function (serverItem) {

        //Logic after start sending data.
        var name = serverItem.name;
        var status = serverItem.status;
        $("#hubStatus").html(status);
        $("#counter").html(sendTimeLimit);
        timeCounter();
        startTimer(proxyHub, name );
    });

    proxyHub.on('forceStopClint', function (serverItem) {


        clearClintPendingTask(serverItem);//Logic before proxy stop.
        $("#btnHub").text("Force Stop...");
        $.connection.hub.stop();
    });

    proxyHub.on('onUserDisconnected', function (serverItem) {
        //Logic after proxy Disconnected (time out).
        $("#hubStatus").html(serverItem);
        $("#btnHub").text("Stat Again after reload window");
   });
}

//Logic before proxy stop.
function clearClintPendingTask(status) {
    //do all you need
    $("#hubStatus").html(status); 
    stopTimer();
}

function startTimer(proxyHub,data) {
    stopTimer();
    proxyTimer = setTimeout(function () {
        proxyHub.server.forceStopServer(data);
    }, sessionTime);
}

function stopTimer() {
    if (proxyTimer) {
        clearTimeout(proxyTimer);
        proxyTimer = null;
    }
}

function timeCounter() {
    var counter = sendTimeLimit;
    var interval = setInterval(function () {
        counter--;
        $("#counter").html(counter);
        if (counter == 0) {
            //Do something
            $("#counter").html("Countdown ended!");
            // Stop the counter
            clearInterval(interval);
        }
    }, 1000);
}

(Testato)



0

Modifica aggiornata, vedere l' opzione 3 di seguito. Tutti gli altri si affidano al timeout, ho pubblicato una disconnessione forzata.

Se stai provando un Force Disconnect - puoi ottenere l'elenco degli Utenti connessi e chiamare la ForceLogOutFunzione sul lato server, l'ho visto da qualche parte sul progetto del codice, spero che ti aiuti. Se vuoi solo forzare Logout / kill di alcuni utenti, esegui semplicemente il loop e uccidi solo quella connessione.

Lato server


public class User
{
    public string Name { get; set; }
    public HashSet<string> ConnectionIds { get; set; }
}

public class ExtendedHub : Hub
{        
   private static readonly ConcurrentDictionary<string, User> ActiveUsers  = 
      new ConcurrentDictionary<string, User>(StringComparer.InvariantCultureIgnoreCase);
    public IEnumerable<string> GetConnectedUsers()
    {
        return ActiveUsers.Where(x => {

            lock (x.Value.ConnectionIds)
            {
                return !x.Value.ConnectionIds.Contains
                        (Context.ConnectionId, StringComparer.InvariantCultureIgnoreCase);
            }

        }).Select(x => x.Key);
    }           

    public void forceLogOut(string to)
    {
        User receiver;
        if (ActiveUsers.TryGetValue(to, out receiver))
        {
            IEnumerable<string> allReceivers;
            lock (receiver.ConnectionIds)
            {
                allReceivers = receiver.ConnectionIds.Concat(receiver.ConnectionIds);      
            }

            foreach (var cid in allReceivers)
            {
             // ***************** log out/KILL connection for whom ever your want here
                Clients.Client(cid).Signout();
            }
        }
    }
}

Dalla parte del cliente

 // 1- Save your connection variable when you start it, and later on you can use it to stop.
var myHubProxy = $.connection.myHub 
// 2- Use it when you need to stop it, IF NOT YOU WILL GET AN ERROR
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

// With a button for testing
$('#SomeButtonKillSignalr').click(function () {
            $.connection.hub.stop();                
        });

Aggiornato con l'opzione 3 : in base alla richiesta ... le altre soluzioni si basano sul timeout, ma puoi anche forzarlo direttamente eliminando la connessione tu stesso

Ho aperto il codice SignalR e al suo interno è possibile vedere DisposeAndRemoveAsyncl'effettiva chiusura di una connessione client.

1- Puoi modificare o chiamare DisposeAndRemoveAsynccon la tua connessione.

2- Quindi chiamare RemoveConnection(connection.ConnectionId);

public async Task DisposeAndRemoveAsync(HttpConnectionContext connection)
        {
            try
            {
                // this will force it
                await connection.DisposeAsync();
            }
            catch (IOException ex)
            {
                _logger.ConnectionReset(connection.ConnectionId, ex);
            }
            catch (WebSocketException ex) when (ex.InnerException is IOException)
            {
                _logger.ConnectionReset(connection.ConnectionId, ex);
            }
            catch (Exception ex)
            {
                _logger.FailedDispose(connection.ConnectionId, ex);
            }
            finally
            {
                // Remove it from the list after disposal so that's it's easy to see
                // connections that might be in a hung state via the connections list
                RemoveConnection(connection.ConnectionId);
            }
        }

Attenzione, fai tutto da solo quando hai finito.


Come ho detto, la funzione $.connection.hub.stop()genera un errore perché la richiesta non è stata completamente inviata al back-end. Quindi sto cercando una soluzione che uccida la connessione attualmente attiva anche se è presente una richiesta in esecuzione.
Snickbrack
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.