【问题标题】:Detecting when a user leaves (i.e. by closing their browser)检测用户何时离开(即通过关闭浏览器)
【发布时间】:2021-08-15 15:25:18
【问题描述】:

我有一个 Singleton 服务 GameServer 跟踪访问特定页面的客人,一个 Game.razor 页面和一个 Scoped 服务 GameBridge,在它们之间。 Gamebridge 注入Game.razorGameServer 注入Gamebridge

GameServer 发出一个 ping 事件,由GameBridge 中继到页面。然后该页面调用Gamebridge 中的一个方法,该方法被中继到GameServer,并将LastSeen DateTime 设置为用户的DateTime.Now。

这个想法是,如果用户关闭他们的浏览器,Scoped 服务将被释放,GameServer 将不再从用户那里获得更新 ping,即用户已超时,游戏可能会被没收,游戏废弃的房间等等。

问题是即使关闭了浏览器,Game.razorGameBridge 也会像一切正常一样继续运行——我可以看到这是因为丢失用户的时间值不断更新.截至目前,即使用户在 blazor 服务器端关闭浏览器,我也无法检测到用户断开连接。

【问题讨论】:

  • 是的,Blazor 服务器。该链接看起来可能是正确的轨道。我会再喝一杯咖啡,看看今晚能不能正常工作。 (这里是午夜,但我很想完成这项工作)
  • @Steve Greene,非常感谢!稍加修改,我就能够在几个浏览器上隔离几个窗口的电路。我很有信心我可以继续前进,我可以放弃所有事件和大部分方法调用。不过我有点担心——为什么GameBridgeGame.razor 仍然被实例化?我是否需要处理所有事件订阅等?
  • @Bennyboy1973 我之前已经通过使用 Signalr 并覆盖集线器中的 ondisconnect 来完成此操作。然而,这是在 WASM 中,但主体是相同的。
  • 谢谢大家。我没有找到很多关于此的示例,而且通常非常好的 MSDN 文档也没有帮助。希望以后有时间,我可以在这里发布一个基本的用户服务器。

标签: c# blazor blazor-server-side


【解决方案1】:

我的解决方案挂钩到浏览器窗口 beforeunload 事件,并使用 Guid 跟踪 SPA 会话。

这是我的演示系统,您可以根据自己的代码进行调整。

添加一个单例服务并注册它。

using System;

namespace Blazor.App.Services
{
    public class SessionTrackerService
    {
        // Session List, blah, blah, ..

        public void NotifyNewSession(Guid ID)
        {
            //  Do whatever you want
            Debug.WriteLine($"Session {ID} Registered");
        }

        public void NotifySessionClosed(Guid ID)
        {
            var x = 0;
            Debug.WriteLine($"Session {ID} Closed");
            // Do re-registering
        }
    }
}

site.js在基本页面中注册。

window.blazor_setExitEvent = function (dotNetHelper) {
    blazor_dotNetHelper = dotNetHelper;
        window.addEventListener("beforeunload", blazor_SetSPAClosed);
}

var blazor_dotNetHelper;

window.blazor_SetSPAClosed = function() {
    blazor_dotNetHelper.invokeMethodAsync('SPASessionClosed')
    blazor_dotNetHelper.dispose();
}

添加一些代码到App.razor。如果您需要在其他地方使用它,请级联 SessionID。调用window.blazor_setExitEvent 设置窗口beforeunload 事件并将其传递给App 的引用。

<CascadingValue Name="SessionID" Value="this.ID">
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        .....
    </Router>
</CascadingValue>

@code {
    [Inject] private IJSRuntime _js { get; set; }
    [Inject] private SessionTrackerService sessionTrackerService { get; set; }
    public Guid ID { get; set; } = Guid.NewGuid();

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            sessionTrackerService.NotifyNewSession(ID);
            var dotNetReference = DotNetObjectReference.Create(this);
            await _js.InvokeVoidAsync("window.blazor_setExitEvent", dotNetReference);
        }
    }

    [JSInvokable("SPASessionClosed")]
    public void SPASessionClosed()
    {
        sessionTrackerService.NotifySessionClosed(ID);
    }

我在 Edge、Chrome 和 Firefox 上进行了测试。

【讨论】:

  • 这是一个看起来不错的解决方案,但是如果有人使用 alt-F4 会发生什么?我正在尝试实现一个供孩子们使用的游戏服务器,他们这样做非常狡猾。我的电路系统工作正常——编程太多以至于无法共享它,并且不确定它是不是矫枉过正。我对此有一个辅助问题——我发现如果我将 CircuitHelper 实现为单例,它会显示所有电路,而 scoped 只给出自己的电路。我想要两个实现——有什么想法吗?
  • @Bennyboy1973。你测试过这个吗? Alt-F4 触发事件的方式与关闭选项卡或浏览器的方式相同。我刚刚测试过了。
  • 不,我没有测试它,因为在我阅读您的答案时,我已经让 CircuitHandler 工作了。我只是在猜测,如果你杀死浏览器,JavaScript 将立即停止运行。
猜你喜欢
  • 2021-11-06
  • 1970-01-01
  • 2015-04-09
  • 2016-09-19
  • 2016-10-05
  • 2018-11-26
  • 2010-12-13
  • 1970-01-01
相关资源
最近更新 更多