如何终止 SignalR 连接?
Posted
技术标签:
【中文标题】如何终止 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 会引发错误,因为请求尚未完全发送到后端。因此,我正在寻找一种解决方案,即使存在正在运行的请求,也会终止当前活动的连接。以上是关于如何终止 SignalR 连接?的主要内容,如果未能解决你的问题,请参考以下文章