Comment puis-je supprimer une connexion SignalR?

10

J'utilise SignalR pour transférer des données sur un site Web. Mais SignalR ne devrait être en mesure d'envoyer des données que pendant une certaine période et si la période s'est écoulée, la connexion doit être interrompue.

La fonction d'arrêt $.connection.hub.stop()est annulée si une demande est toujours en attente et n'est pas terminée. Mais cette demande doit être forcée d'annuler, quelle que soit la quantité de données envoyée.

Comment puis-je supprimer une connexion SignalR?

Snickbrack
la source

Réponses:

6

Comme vous pouvez le voir dans cette documentation Microsoft sur les paramètres Timeout et Keepalive, vous pouvez définir DisconnectTimeout dans les options.

Exemple:

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

Edit : Puisque vous voulez couper la connexion du client quoi CancellationTokenqu'il arrive , vous parlez d'un comportement mais malheureusement cela n'est toujours pas pris en charge dans SignalR comme vous pouvez le voir ici et ici , l'équipe veut le faire pour SignalRmais il y a toujours aucune nouvelle à ce sujet.

Kiril1512
la source
Comme je l'ai indiqué, la demande du Frontend-Site n'est pas terminée, il reste donc des données du frontend à envoyer au SignalR-Backend / Hub. Je recherche donc une solution frontend car une quantité décente de données est envoyée et si une période de temps est écoulée, la connexion doit être interrompue par le frontend, que les données aient été transmises ou non. Comprenez-vous ce que je recherche?
Snickbrack
@Snickbrack, vous voulez couper la connexion via le côté client, même si vous envoyez des données en ce moment, j'ai raison?
Kiril1512
1
Oui. Vous avez raison.
Snickbrack
@Snickbrack a mis à jour ma réponse.
Kiril1512
@Snickbrack n'oubliez pas de sélectionner la bonne réponse à votre question, ceci ou d'autres réponses ici ...
Kiril1512
1

Veuillez lire ce document Microsoft sur l'événement de durée de vie du concentrateur. Vous pouvez modifier les valeurs par défaut de ces paramètres, les définir Application_Startdans votre Global.asaxfichier. Mais de cette façon, vous ne pouvez pas contrôler complètement le côté client. Donc, vous utilisez la setTimeoutfonction javascript et passez la fin du serveur de formulaire de temps quand un nouvel utilisateur connect.it peut être GlobalHost.Configuration.DisconnectTimeoutou quand vous le souhaitez. Je donne un exemple complet avec un projet de démonstration. En fait, j'utilise cette logique dans un très grand système de billetterie pour la détention de ticket en temps réel. (veuillez lire tous les commentaires en ligne)

Modèle:

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

Centre:

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

(Testé)

Ashiquzzaman
la source
0

Vous devez définir un délai d'expiration. Sur le serveur, vous pouvez définir DisconnectTimeout, comme ceci:

GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromMinutes(30);

https://zzz.buzz/2016/05/11/setting-timeout-for-signalr-for-easier-debugging/

Lajos Arpad
la source
Comme je l'ai indiqué, la demande du Frontend-Site n'est pas terminée, il reste donc des données du frontend à envoyer au SignalR-Backend / Hub. Je recherche donc une solution frontale.
Snickbrack
0

Modification mise à jour, veuillez consulter l' option 3 ci-dessous. Tous les autres comptent sur le timeout, j'ai posté une déconnexion forcée.

Si vous essayez de forcer la déconnexion - vous pouvez obtenir la liste des utilisateurs connectés et appeler la ForceLogOutfonction côté serveur, j'ai vu cela quelque part sur le projet de code, j'espère que cela aide. Si vous souhaitez uniquement forcer la déconnexion / tuer certains utilisateurs, il suffit de parcourir et de tuer uniquement cette connexion.

Du côté serveur


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

Côté client

 // 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();                
        });

Mise à jour avec l'option 3 : sur demande ... les autres solutions reposent sur le time out, mais vous pouvez également le forcer directement en supprimant la connexion vous-même

J'ai ouvert le code SignalR et à l'intérieur, vous pouvez voir DisposeAndRemoveAsyncla fin réelle d'une connexion client.

1- Vous pouvez modifier ou appeler DisposeAndRemoveAsyncavec votre connexion.

2- Appelez ensuite 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);
            }
        }

Attention, nettoyez vous-même lorsque cela est fait.

transformateur
la source
Comme je l'ai indiqué, la $.connection.hub.stop()fonction renvoie une erreur car la demande n'a pas été entièrement envoyée au backend. Je recherche donc une solution qui tue la connexion actuellement active même s'il y a une demande en cours.
Snickbrack