Wie kann ich eine SignalR-Verbindung beenden?

10

Ich verwende SignalR, um Daten auf einer Website zu übertragen. SignalR sollte jedoch nur für einen bestimmten Zeitraum Daten senden können. Wenn der Zeitraum abgelaufen ist, sollte die Verbindung unterbrochen werden.

Die Stop-Funktion $.connection.hub.stop()wird abgebrochen, wenn eine Anfrage noch aussteht und nicht abgeschlossen ist. Diese Anfrage sollte jedoch abgebrochen werden, unabhängig davon, wie viele Daten gesendet wurden.

Wie kann ich töte eine SignalR-Verbindung?

Snickbrack
quelle

Antworten:

6

Wie Sie in dieser Microsoft-Dokumentation zu Timeout- und Keepalive-Einstellungen sehen können, können Sie das DisconnectTimeout in den Optionen definieren.

Beispiel:

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

Bearbeiten : Da Sie die Verbindung vom Client CancellationTokenauf jeden Fall trennen möchten, sprechen Sie über ein Verhalten, aber leider wird dies in SignalR immer noch nicht unterstützt, wie Sie hier und hier sehen können. Das Team möchte dies tun, SignalRaber es gibt es immer noch Keine Neuigkeiten darüber.

Kiril1512
quelle
Wie bereits erwähnt, ist die Anforderung von der Frontend-Site nicht abgeschlossen, sodass noch einige Daten vom Frontend an das SignalR-Backend / Hub gesendet werden müssen. Ich suche also nach einer Frontend-Lösung, weil eine anständige Datenmenge gesendet wird und wenn eine Zeitspanne verstrichen ist, sollte die Verbindung vom Frontend unterbrochen werden, unabhängig davon, ob die Daten übertragen wurden oder nicht. Verstehst du was ich suche?
Snickbrack
@Snickbrack Sie möchten die Verbindung über den Client beenden, auch wenn Sie gerade Daten senden, bin ich richtig?
Kiril1512
1
Ja. Du hast Recht.
Snickbrack
@ Nickbrack hat meine Antwort aktualisiert.
Kiril1512
@Snickbrack vergessen Sie nicht, die richtige Antwort auf Ihre Frage, diese oder andere Antworten hier
auszuwählen
1

Bitte lesen Sie dieses Microsoft-Dokument über das Hub-Lebenszeitereignis. Sie können die Standardwerte für diese Einstellungen ändern und Application_Startin Ihrer Global.asaxDatei festlegen . Auf diese Weise können Sie die Clientseite jedoch nicht vollständig steuern. Sie verwenden also die Javascript- setTimeoutFunktion und übergeben die Zeit vom Serverende, wenn ein neuer Benutzer eine Verbindung herstellt GlobalHost.Configuration.DisconnectTimeout. Ich gebe ein vollständiges Beispiel mit Demo-Projekt. Eigentlich verwende ich diese Logik in einem sehr großen Ticketsystem für das Halten von Tickets in Echtzeit. (Bitte lesen Sie alle Inline-Kommentare)

Modell:

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

Nabe:

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

(Geprüft)

Ashiquzzaman
quelle
0

Sie müssen ein Timeout definieren. Auf dem Server können Sie DisconnectTimeout wie folgt festlegen:

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

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

Lajos Arpad
quelle
Wie bereits erwähnt, ist die Anforderung von der Frontend-Site nicht abgeschlossen, sodass noch einige Daten vom Frontend an das SignalR-Backend / Hub gesendet werden müssen. Ich suche also eine Frontend-Lösung.
Snickbrack
0

Aktualisierte Bearbeitung, siehe Option 3 unten. Alle anderen verlassen sich auf Timeout, ich habe eine erzwungene Trennung gepostet.

Wenn Sie versuchen, eine Verbindung zu erzwingen, können Sie die Liste der verbundenen Benutzer abrufen und die ForceLogOutFunktion auf der Serverseite aufrufen. Ich habe dies irgendwo im Codeprojekt gesehen. Ich hoffe, es hilft. Wenn Sie nur einige der Benutzer erzwingen / beenden möchten, durchlaufen Sie einfach diese Verbindung und beenden Sie sie nur.

Serverseite


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

Client-Seite

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

Aktualisiert mit Option 3 : basierend auf Anfrage ... die anderen Lösungen basieren auf einer Zeitüberschreitung, aber Sie können sie auch direkt erzwingen, indem Sie die Verbindung selbst trennen

Ich habe den SignalR-Code geöffnet und darin können Sie DisposeAndRemoveAsyncdie tatsächliche Beendigung einer Client-Verbindung sehen.

1- Sie können DisposeAndRemoveAsyncIhre Verbindung ändern oder anrufen .

2- Dann anrufen 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);
            }
        }

Achtung, räumen Sie selbst auf, wenn dies erledigt ist.

Transformator
quelle
Wie bereits erwähnt, gibt die $.connection.hub.stop()Funktion einen Fehler aus, da die Anforderung nicht vollständig an das Backend gesendet wurde. Daher suche ich nach einer Lösung, die die derzeit aktive Verbindung beendet, selbst wenn eine laufende Anforderung vorliegt.
Snickbrack