Como posso matar uma conexão SignalR?

10

Estou usando o SignalR para transferir dados em um site. Mas o SignalR deve poder enviar dados apenas por um período e, se o período tiver passado, a conexão deverá ser interrompida.

A função Stop $.connection.hub.stop()é cancelada se uma solicitação ainda estiver pendente e não for concluída. Mas essa solicitação deve ser forçada a cancelar, independentemente da quantidade de dados enviada.

Como posso matar uma conexão SignalR?

Snickbrack
fonte

Respostas:

6

Como você pode ver nesta documentação da Microsoft sobre configurações de tempo limite e manutenção da atividade, você pode definir o DisconnectTimeout nas opções.

Exemplo:

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 : Como você deseja eliminar a conexão do cliente, não importa o quê, você está falando sobre um CancellationTokencomportamento, mas infelizmente isso ainda não é suportado no SignalR, como você pode ver aqui e aqui , a equipe quer fazer isso, SignalRmas ainda há nenhuma notícia sobre isso.

Kiril1512
fonte
Como afirmei, a solicitação do site de front-end não foi concluída, portanto ainda existem alguns dados do front-end a serem enviados ao SignalR-Backend / Hub. Portanto, estou procurando uma solução de front-end porque uma quantidade decente de dados é enviada e se um período de tempo decorrido, a conexão deve ser interrompida pelo front-end, independentemente de os dados terem sido transmitidos ou não. Você entende o que estou procurando?
Snickbrack 31/03
@ Snickbrack você quer matar a conexão via lado do cliente, mesmo se você estiver enviando dados agora, estou certo?
Kiril1512 31/03
11
sim. Você está certo.
Snickbrack 31/03
@Snickbrack atualizou minha resposta.
Kiril1512
@ Snickbrack não se esqueça de selecionar a resposta correta para sua pergunta, esta ou outras respostas aqui ...
Kiril1512 17/04
1

Leia este documento da microsoft sobre o evento de duração do Hub. Você pode alterar os valores padrão para essas configurações, defini-los Application_Startem seu Global.asaxarquivo. Mas desta forma você não pode controlar totalmente o lado do cliente. Então você usa a setTimeoutfunção javascript e passa o horário do servidor quando um novo usuário connect.it pode ser GlobalHost.Configuration.DisconnectTimeoutou quando você quiser. Dou um exemplo completo com o projeto de demonstração. Na verdade, eu uso essa lógica em um sistema de emissão de bilhetes muito grande para guardar bilhetes em tempo real. (leia todos os comentários em linha)

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

(Testado)

Ashiquzzaman
fonte
0

Você precisa definir um tempo limite. No servidor, você pode definir DisconnectTimeout, assim:

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

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

Lajos Arpad
fonte
Como afirmei, a solicitação do site de front-end não foi concluída, portanto ainda existem alguns dados do front-end a serem enviados ao SignalR-Backend / Hub. Então, estou procurando uma solução de front-end.
Snickbrack 31/03
0

Edição atualizada, consulte a opção 3 abaixo.Todos os outros estão contando com o tempo limite, eu postei uma desconexão forçada.

Se você estiver tentando uma desconexão forçada - poderá obter a lista dos usuários conectados e ligar para o ForceLogOut função no lado do servidor. Vi isso em algum lugar no projeto de código, espero que ajude. Se você quiser forçar o logout / matar apenas alguns dos usuários, faça um loop e elimine somente essa conexão.

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

Atualizado com a opção 3 : com base em solicitação ... as outras soluções dependem do tempo limite, mas você também pode forçá-lo diretamente , descartando a conexão por conta própria

Abri o código SignalR e, dentro, você pode ver DisposeAndRemoveAsynco encerramento real de uma conexão de cliente.

1- Você pode modificar ou ligar DisposeAndRemoveAsynccom sua conexão.

2- Então ligue 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);
            }
        }

Cuidado, limpe-se quando isso for feito.

transformador
fonte
Como afirmei, a $.connection.hub.stop()função-gera um erro porque a solicitação não foi totalmente enviada ao back-end. Portanto, estou procurando uma solução que mate a conexão ativa no momento, mesmo que exista uma solicitação em execução.
Snickbrack 31/03