如何终止 SignalR 连接?

     2023-02-22     176

关键词:

【中文标题】如何终止 SignalR 连接?【英文标题】:How can I kill a SignalR connection? 【发布时间】:2020-07-04 03:33:22 【问题描述】:

我正在使用 SignalR 在网站上传输数据。但是 SignalR 应该只能在一段时间内发送数据,如果该时间段已过,则应终止连接。

如果请求仍处于挂起状态且未完成,则停止功能 $.connection.hub.stop() 将被取消。但是无论发送了多少数据,这个请求都应该被强制取消。

我如何杀死 SignalR-Connection?

【问题讨论】:

【参考方案1】:

正如您在 Microsoft Documentation about Timeout and keepalive settings 中看到的,您可以在选项中定义 DisconnectTimeout。

例子:

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

编辑:由于无论如何您都想终止来自客户端的连接,因此您正在谈论 CancellationToken 行为,但不幸的是,这在 SignalR 中仍然不支持,如您所见 @987654322 @ 和here,团队想对SignalR 这样做,但仍然没有任何消息。

【讨论】:

正如我所说,来自前端站点的请求尚未完成,因此前端仍有一些数据要发送到 SignalR-Backend/Hub。所以我正在寻找一个前端解决方案,因为发送了大量的数据,如果经过一段时间,那么无论数据是否已经传输,前端都应该终止连接。你明白我在找什么吗? @Snickbrack 你想通过客户端终止连接,即使你现在正在发送数据,我是对的? @Snickbrack 不要忘记选择您问题的正确答案,这里或其他答案...【参考方案2】:

请阅读this microsoft 关于 Hub 生命周期事件的文档。您可以更改这些设置的默认值,在您的Global.asax 文件中的Application_Start 中设置它们。但是这样你就不能完全控制客户端。因此,您使用 javascript setTimeout 函数并在新用户连接时从服务器端传递时间。它可能是 GlobalHost.Configuration.DisconnectTimeout 或您想要的任何时间。我用演示项目给出了一个完整的例子。实际上我在一个非常大的票务系统中使用这个逻辑来实时持有票。(请阅读所有内联评论)

型号:

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

中心:

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



测试视图:

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

(已测试)

【讨论】:

【参考方案3】:

您需要定义超时。在服务器上你可以设置 DisconnectTimeout,像这样:

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

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

【讨论】:

正如我所说,来自前端站点的请求尚未完成,因此前端仍有一些数据要发送到 SignalR-Backend/Hub。所以我正在寻找前端解决方案。【参考方案4】:

更新编辑,请参阅下面的选项 3所有其他人都依赖超时,我发布了强制断开连接。

如果您正在尝试强制断开连接 - 您可以获取已连接用户的列表并在服务器端调用 ForceLogOut 函数,我在代码项目的某个地方看到了这个,希望对您有所帮助。如果您只想强制注销/杀死某些用户,只需循环并仅终止该连接即可。

服务器端


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


客户端

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

使用选项 3 更新:根据请求...其他解决方案依赖于超时,但您也可以直接强制通过自行处理连接 p>

我打开 SignalR 代码,在里面你可以看到 DisposeAndRemoveAsync 客户端连接的实际终止。

1- 您可以通过您的连接修改或致电DisposeAndRemoveAsync

2- 然后拨打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);
            
        

注意,完成后请自行清理。

【讨论】:

正如我所说,$.connection.hub.stop()-Function 会引发错误,因为请求尚未完全发送到后端。因此,我正在寻找一种解决方案,即使存在正在运行的请求,也会终止当前活动的连接。

如何从angularJs连接signalR

】如何从angularJs连接signalR【英文标题】:HowtoconnectsignalRfromangularJs【发布时间】:2017-02-0100:50:02【问题描述】:我在.NET中将Web应用程序开发为两个独立的应用程序,后端使用webapic#,用户界面使用AngularJS。我只想在这个项目中添... 查看详情

如何确定与 SignalR 客户端的服务器断开连接?

】如何确定与SignalR客户端的服务器断开连接?【英文标题】:HowtodetermineserverdisconnectionfromSignalRclient?【发布时间】:2012-02-2410:36:18【问题描述】:SignalRJavaScript客户端如何检测与服务器的连接何时丢失?【问题讨论】:你在asp.ne... 查看详情

SignalR:服务器如何建立与客户端的连接?

】SignalR:服务器如何建立与客户端的连接?【英文标题】:SignalR:howServercanestablishconnectiontoClient?【发布时间】:2017-05-1110:43:18【问题描述】:有没有可能实现这种逻辑:服务器与客户端建立连接。那么客户端使用这个连接向服... 查看详情

SignalR Client 开始连接时如何设置用户?

】SignalRClient开始连接时如何设置用户?【英文标题】:SignalRClientHowtoSetuserwhenstartconnection?【发布时间】:2015-06-1302:25:27【问题描述】:服务器端:publicoverrideTaskOnConnected()varconnectionId=Context.ConnectionId;varuser=Context.User.Identity.Name;// 查看详情

使用一个连接发送并发 SignalR 消息

】使用一个连接发送并发SignalR消息【英文标题】:SendconcurrentSignalRmessageswithoneconnection【发布时间】:2022-01-0611:56:43【问题描述】:如何仅通过一个连接并行发送多条SignalR消息?我有以下集线器方法:publicasyncTaskTest()Console.WriteLi... 查看详情

SignalR:检测客户端上的连接状态

】SignalR:检测客户端上的连接状态【英文标题】:SignalR:detectconnectionstateonclient【发布时间】:2012-03-0905:48:02【问题描述】:我已经了解了如何通过绑定到.disconnect事件来使用SignalR在客户端捕获断开连接事件。现在我已经完成了... 查看详情

如何从signalr客户端端确定服务器断开连接

...t;disconnected");对持续连接可以覆盖OnDisconnectAsync,(从SignalR维基于)publicclassMyEndPoint:PersistentConnectionprotectedoverrideTaskOnDisconnectAsync(stringclientId)returnConnection.Broadcast("Client"+clientId+"disconncte 查看详情

如何使用 SignalR 加入群组

】如何使用SignalR加入群组【英文标题】:HowtojoinagroupusingSignalR【发布时间】:2013-06-2223:35:07【问题描述】:我是使用SignalR的新手(从今天开始),向所有连接的客户端发送消息非常简单,但现在我只想发送给一个组。我找不到... 查看详情

如何通过 SignalR 实现“谁在打字”功能?

】如何通过SignalR实现“谁在打字”功能?【英文标题】:Howtoimplement\'Whoistyping\'featureviaSignalR?【发布时间】:2014-11-0317:14:29【问题描述】:基本上,我正在我的网站中实现SignalR聊天。我已经可以向所有连接的用户发送消息,现... 查看详情

signalr设计理念(代码片段)

SignalR设计理念(二)实现客户端和服务器端的实时通讯.前言:客户端方法忽略大小写,主要原因基于是URL对大小写不敏感的问题,开发者之间为了更好的协同开发,定下的开发者协议。问题阐述客户端数量不确定!同一个用户的客... 查看详情

signalr和node.js哪个好

在你使用signalR的过程中一定已经注意到了,前端页面除了加载signalR外还需要加载~/signalr/hubs下的js文件。该自动生成的文件指明了signalR客户端该如何访问后端。当前端的signalRClient执行connect方法时其与后端建立了一个连接(连接... 查看详情

Blazor WebAssembly SignalR 身份验证

】BlazorWebAssemblySignalR身份验证【英文标题】:BlazorWebAssemblySignalRAuthentication【发布时间】:2020-09-1608:28:49【问题描述】:我希望看到有关如何使用Blazor的WebAssembly风格向SignalR集线器连接添加身份验证的示例。我的dotnet版本是3.1.300... 查看详情

SignalR .Net Core 多连接

】SignalR.NetCore多连接【英文标题】:SignalR.NetCoreMultipleConnection【发布时间】:2020-08-1712:25:44【问题描述】:您好,我的应用程序后端是.NETCore,前端是Angular2+。我有一页,两个人既可以聊天又可以查看图表中的报告。我想用signalR... 查看详情

signalR 背板是不是也共享连接?

】signalR背板是不是也共享连接?【英文标题】:DoessignalRbackplanesharesconnectionsalso?signalR背板是否也共享连接?【发布时间】:2015-06-0112:05:34【问题描述】:signalR背板是否也共享连接信息?我的意思是在“longpolling”的情况下,连... 查看详情

SignalR 和 OpenId 连接

】SignalR和OpenId连接【英文标题】:SignalRandOpenIdConnect【发布时间】:2017-04-0922:11:20【问题描述】:我有一个使用ASP.NETCoreWebApi和OpenIddict作为授权框架的服务器。现在我添加了一个SignalR主机,并想为其添加授权。从differentsources我... 查看详情

SignalR 和 OpenId 连接

】SignalR和OpenId连接【英文标题】:SignalRandOpenIdConnect【发布时间】:2017-04-0922:11:20【问题描述】:我有一个使用ASP.NETCoreWebApi和OpenIddict作为授权框架的服务器。现在我已经添加了一个SignalR主机,并想为其添加授权。从differentsourc... 查看详情

SignalR:为啥选择集线器与持久连接?

】SignalR:为啥选择集线器与持久连接?【英文标题】:SignalR:WhychooseHubvs.PersistentConnection?SignalR:为什么选择集线器与持久连接?【发布时间】:2012-03-0600:47:56【问题描述】:我最近一直在搜索和阅读SignalR,虽然我看到很多关于... 查看详情

如何在 ng2-signalr 中设置授权标头?

】如何在ng2-signalr中设置授权标头?【英文标题】:HowtosetAuthorizationHeaderinng2-signalr?【发布时间】:2017-10-3121:24:49【问题描述】:我在ionic2中使用库ng2-signalr。问题是我不知道如何设置授权标头。我已经搜索但没有找到任何示例。... 查看详情