¿Cómo puedo eliminar una conexión SignalR?

10

Estoy usando SignalR para transferir datos en un sitio web. Pero SignalR solo debería poder enviar datos durante un período de tiempo y, si el período de tiempo ha pasado, la conexión debe interrumpirse.

La función de detención $.connection.hub.stop()se cancela si una solicitud aún está pendiente y no se completa. Pero esta solicitud debe ser forzada a cancelar sin importar cuántos datos se hayan enviado.

¿Cómo puedo matar una conexión SignalR?

Snickbrack
fuente

Respuestas:

6

Como puede ver en esta Documentación de Microsoft sobre el tiempo de espera y la configuración de keepalive , puede definir el Tiempo de desconexión en las opciones.

Ejemplo:

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

Editar : dado que desea eliminar la conexión del cliente sin importar qué, está hablando de un CancellationTokencomportamiento, pero desafortunadamente esto aún no es compatible con SignalR, como puede ver aquí y aquí , el equipo quiere hacerlo, SignalRpero aún existe No hay noticias al respecto.

Kiril1512
fuente
Como dije, la solicitud del Frontend-Site no se completó, por lo que todavía hay algunos datos de la interfaz que se enviarán al SignalR-Backend / Hub. Por lo tanto, estoy buscando una solución de interfaz de usuario porque se envía una cantidad decente de datos y si transcurre un período de tiempo, la interfaz debe interrumpir la conexión sin importar si los datos se han transmitido o no. ¿Entiendes lo que estoy buscando?
Snickbrack
@Snickbrack desea eliminar la conexión a través del lado del cliente, incluso si está enviando datos en este momento, ¿verdad?
Kiril1512
1
si. Estás en lo correcto.
Snickbrack
@Snickbrack actualizó mi respuesta.
Kiril1512
@Snickbrack no olvide seleccionar la respuesta correcta a su pregunta, esta u otras respuestas aquí ...
Kiril1512
1

Lea este documento de Microsoft sobre el evento de por vida de Hub. Puede cambiar los valores predeterminados para esta configuración, establecerlos Application_Starten su Global.asaxarchivo. Pero de esta manera no puedes controlar completamente el lado del cliente. Por lo tanto, utiliza la setTimeoutfunción de JavaScript y pasa el tiempo de finalización del servidor cuando un nuevo usuario se conecta o puede hacerlo en GlobalHost.Configuration.DisconnectTimeoutcualquier momento que desee. Doy un ejemplo completo con proyecto de demostración. En realidad, uso esta lógica en un sistema de tickets muy grande para boleto de retención en tiempo real. (por favor lea todos los comentarios en línea)

Modelo:

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

Cubo:

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

(Probado)

Ashiquzzaman
fuente
0

Necesita definir un tiempo de espera. En el servidor puede configurar DisconnectTimeout, de esta manera:

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

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

Lajos Arpad
fuente
Como dije, la solicitud del Frontend-Site no se completó, por lo que todavía hay algunos datos del frontend que se enviarán al SignalR-Backend / Hub. Así que estoy buscando una solución frontend.
Snickbrack
0

Edición actualizada, consulte la Opción 3 a continuación. Todos los demás dependen del tiempo de espera, publiqué una desconexión forzada.

Si está intentando una desconexión forzada, puede obtener la lista de los usuarios conectados y llamar a la ForceLogOutfunción en el lado del servidor, lo vi en algún lugar del proyecto de código, espero que ayude. Si solo desea forzar el cierre de sesión / matar a algunos de los usuarios, simplemente realice un bucle y elimine solo esa conexión.

Lado del servidor


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

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

Actualizado con la opción 3 : según la solicitud ... las otras soluciones dependen del tiempo de espera, pero también puede forzarlo directamente eliminando la conexión usted mismo

Abrí el código SignalR, y dentro de usted puede ver DisposeAndRemoveAsyncla terminación real de una conexión de cliente.

1- Puedes modificar o llamar DisposeAndRemoveAsynccon tu conexión.

2- Entonces llama 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);
            }
        }

Precaución, limpie usted mismo cuando haya terminado.

transformador
fuente
Como dije, la función $.connection.hub.stop()arroja un error porque la solicitud no se ha enviado completamente al Backend. Por lo tanto, estoy buscando una solución que elimine la conexión actualmente activa, incluso si hay una solicitud en ejecución.
Snickbrack