首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >关闭/断开ASP.NET核心signalR客户端连接的正确方法是什么?

关闭/断开ASP.NET核心signalR客户端连接的正确方法是什么?
EN

Stack Overflow用户
提问于 2020-10-20 16:33:40
回答 1查看 3.2K关注 0票数 2

我是一个新用户,正在努力从ASP.NET Core Blazor服务器页面优雅地关闭辅助signalR客户端。

我在第一次渲染Blazor服务器页面时设置了一个辅助signalR客户端连接。当页面通过浏览器选项卡关闭时,我正在尝试关闭此辅助signalR客户端连接。

在写这篇文章的时候

似乎不会在通过浏览器选项卡关闭页面时触发。但是,

方法

触发了。此外,在Safari 13.0.5中,

方法是否在关闭浏览器选项卡时不被触发?Opera、Firefox和Chrome都有

在关闭浏览器选项卡时触发。通过macOS Catalina v10.15.7将Safari更新到v14.0 (15610.1.28.9,15610)修复了此问题。

目前,我正在打电话给

来自

若要关闭signalR连接,请执行以下操作。我使用以下代码关闭客户端连接:

代码语言:javascript
运行
AI代码解释
复制
...
Logger.LogInformation("Closing secondary signalR connection...");
await hubConnection.StopAsync();
Logger.LogInformation("Closed secondary signalR connection");
...

The The The

方法显示为阻塞,即没有为

“已关闭辅助signalR连接”

..。虽然,

我的服务器集线器的处理程序显示连接已断开。这类似于

行为

在这篇文章中描述了

问题

..。

如何正确处理signalR连接

ASP.NET核心3.1

完整的代码清单如下:

处理signalR连接

代码语言:javascript
运行
AI代码解释
复制
#region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// 
        /// Clear secondary signalR Closed event handler and stop the
        /// secondary signalR connection
        /// 
        /// 
        /// ASP.NET Core Release Candidate 5 calls DisposeAsync when 
        /// navigating away from a Blazor Server page. Until the 
        /// release is stable DisposeAsync will have to be triggered from
        /// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
        /// in Dispose().
        /// However, providing DisposeAsync() now makes the migration easier
        /// https://github.com/dotnet/aspnetcore/issues/26737
        /// https://github.com/dotnet/aspnetcore/issues/9960
        /// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
        /// 
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                Logger.LogInformation("Index.razor page is disposing...");

                try
                {
                    if (hubConnection != null)
                    {
                        Logger.LogInformation("Removing signalR client event handlers...");
                        hubConnection.Closed -= CloseHandler;
                    }

                    // Until ASP.NET Core 5 is released in November
                    // trigger DisposeAsync(). See docstring and DiposeAsync() below.
                    // not ideal, but having to use GetAwaiter().GetResult() until
                    // forthcoming release of ASP.NET Core 5 for the introduction
                    // of triggering DisposeAsync on pages that implement IAsyncDisposable
                    DisposeAsync().GetAwaiter().GetResult();
                }
                catch (Exception exception)
                {
                    Logger.LogError($"Exception encountered while disposing Index.razor page :: {exception.Message}");
                }
            }

            disposed = true;
        }


        /// 
        /// Dispose the secondary backend signalR connection
        /// 
        /// 
        /// ASP.NET Core Release Candidate 5 adds DisposeAsync when 
        /// navigating away from a Blazor Server page. Until the 
        /// release is stable DisposeAsync will have to be triggered from
        /// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
        /// in Dispose().
        /// However, providing DisposeAsync() now makes the migration easier
        /// https://github.com/dotnet/aspnetcore/issues/26737
        /// https://github.com/dotnet/aspnetcore/issues/9960
        /// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
        /// 
        public async virtual ValueTask DisposeAsync()
        {
            try
            {
                if (hubConnection != null)
                {
                    Logger.LogInformation("Closing secondary signalR connection...");
                    await hubConnection.StopAsync();
                    Logger.LogInformation("Closed secondary signalR connection");
                }
                // Dispose(); When migrated to ASP.NET Core 5 let DisposeAsync trigger Dispose
            }
            catch (Exception exception)
            {
                Logger.LogInformation($"Exception encountered wwhile stopping secondary signalR connection :: {exception.Message}");
            }
        }
        #endregion

Blazor服务器的完整代码页

代码语言:javascript
运行
AI代码解释
复制
using System;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using WebApp.Data;
using WebApp.Data.Serializers.Converters;
using WebApp.Data.Serializers.Converters.Visitors;
using WebApp.Repository.Contracts;



namespace WebApp.Pages
{
    public partial class Index : IAsyncDisposable, IDisposable
    {
        private HubConnection hubConnection;
        public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
        private bool disposed = false;


        [Inject]
        public NavigationManager NavigationManager { get; set; }
        [Inject]
        public IMotionDetectionRepository Repository { get; set; }
        [Inject]
        public ILogger LoggerMotionDetection { get; set; }
        [Inject]
        public ILogger LoggerMotionInfo { get; set; }
        [Inject]
        public ILogger LoggerJsonVisitor { get; set; }
        [Inject]
        public ILogger Logger { get; set; }


        #region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// 
        /// Clear secondary signalR Closed event handler and stop the
        /// secondary signalR connection
        /// 
        /// 
        /// ASP.NET Core Release Candidate 5 calls DisposeAsync when 
        /// navigating away from a Blazor Server page. Until the 
        /// release is stable DisposeAsync will have to be triggered from
        /// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
        /// in Dispose().
        /// However, providing DisposeAsync() now makes the migration easier
        /// https://github.com/dotnet/aspnetcore/issues/26737
        /// https://github.com/dotnet/aspnetcore/issues/9960
        /// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
        /// 
        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                Logger.LogInformation("Index.razor page is disposing...");

                try
                {
                    if (hubConnection != null)
                    {
                        Logger.LogInformation("Removing signalR client event handlers...");
                        hubConnection.Closed -= CloseHandler;
                    }

                    // Until ASP.NET Core 5 is released in November
                    // trigger DisposeAsync(). See docstring and DiposeAsync() below.
                    // not ideal, but having to use GetAwaiter().GetResult() until
                    // forthcoming release of ASP.NET Core 5 for the introduction
                    // of triggering DisposeAsync on pages that implement IAsyncDisposable
                    DisposeAsync().GetAwaiter().GetResult();
                }
                catch (Exception exception)
                {
                    Logger.LogError($"Exception encountered while disposing Index.razor page :: {exception.Message}");
                }
            }

            disposed = true;
        }


        /// 
        /// Dispose the secondary backend signalR connection
        /// 
        /// 
        /// ASP.NET Core Release Candidate 5 adds DisposeAsync when 
        /// navigating away from a Blazor Server page. Until the 
        /// release is stable DisposeAsync will have to be triggered from
        /// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
        /// in Dispose().
        /// However, providing DisposeAsync() now makes the migration easier
        /// https://github.com/dotnet/aspnetcore/issues/26737
        /// https://github.com/dotnet/aspnetcore/issues/9960
        /// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
        /// 
        public async virtual ValueTask DisposeAsync()
        {
            try
            {
                if (hubConnection != null)
                {
                    Logger.LogInformation("Closing secondary signalR connection...");
                    await hubConnection.StopAsync();
                    Logger.LogInformation("Closed secondary signalR connection");
                }
                // Dispose(); When migrated to ASP.NET Core 5 let DisposeAsync trigger Dispose
            }
            catch (Exception exception)
            {
                Logger.LogInformation($"Exception encountered wwhile stopping secondary signalR connection :: {exception.Message}");
            }
        }
        #endregion


        #region ComponentBase

        /// 
        /// Connect to the secondary signalR hub after rendering.
        /// Perform on the first render. 
        /// 
        /// 
        /// This could have been performed in OnInitializedAsync but
        /// that method gets executed twice when server prerendering is used.
        /// 
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                var hubUrl = NavigationManager.BaseUri.TrimEnd('/') + "/motionhub";

                try
                {
                    Logger.LogInformation("Index.razor page is performing initial render, connecting to secondary signalR hub");

                    hubConnection = new HubConnectionBuilder()
                        .WithUrl(hubUrl)
                        .ConfigureLogging(logging =>
                        {
                            logging.AddConsole();
                            logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Information);
                        })
                        .AddJsonProtocol(options =>
                        {
                            options.PayloadSerializerOptions = JsonConvertersFactory.CreateDefaultJsonConverters(LoggerMotionDetection, LoggerMotionInfo, LoggerJsonVisitor);
                        })
                        .Build();

                    hubConnection.On("ReceiveMotionDetection", ReceiveMessage);
                    hubConnection.Closed += CloseHandler;

                    Logger.LogInformation("Starting HubConnection");

                    await hubConnection.StartAsync();

                    Logger.LogInformation("Index Razor Page initialised, listening on signalR hub url => " + hubUrl.ToString());
                }
                catch (Exception e)
                {
                    Logger.LogError(e, "Encountered exception => " + e);
                }
            }
        }

        protected override async Task OnInitializedAsync()
        {
            await Task.CompletedTask;
        }
        #endregion


        #region signalR

        /// Log signalR connection closing
        /// 
        /// If an exception occurred while closing then this argument describes the exception
        /// If the signaR connection was closed intentionally by client or server, then this
        /// argument is null
        /// 
        private Task CloseHandler(Exception exception)
        {
            if (exception == null)
            {
                Logger.LogInformation("signalR client connection closed");
            }
            else
            {
                Logger.LogInformation($"signalR client closed due to error => {exception.Message}");
            }

            return Task.CompletedTask;
        }

        /// 
        /// Add motion detection notification to repository
        /// 
        /// Motion detection received via signalR
        private void ReceiveMessage(MotionDetection message)
        {
            try
            {
                Logger.LogInformation("Motion detection message received");

                Repository.AddItem(message);

                StateHasChanged();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "An exception was encountered => " + ex.ToString());
            }
        }
        #endregion
    }
}

signalR服务器集线器

代码语言:javascript
运行
AI代码解释
复制
using System;
using System.Threading.Tasks;

using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;


namespace WebApp.Realtime.SignalR
{
    /// 
    /// This represents endpoints available on the server, available for the
    /// clients to call
    /// 
    public class MotionHub : Hub
    {
        private bool _disposed = false;
        public ILogger Logger { get; set; }

        public MotionHub(ILogger logger) : base()
        {
            Logger = logger;
        }


        public override async Task OnConnectedAsync()
        {
            Logger.LogInformation($"OnConnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name}");
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            if (exception != null)
            {
                Logger.LogInformation($"OnDisconnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name} : Exception={exception.Message}");
            }
            else
            {
                Logger.LogInformation($"OnDisconnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name}");
            }

            await base.OnDisconnectedAsync(exception);
        }

        // Protected implementation of Dispose pattern.
        protected override void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            // Call base class implementation.
            base.Dispose(disposing);
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-10-21 13:09:58

在帮助下修复

ASP.NET核心Github讨论

..。

方法已替换

这调用

而无需等待任务结果。

我还更新了停止集线器连接的代码:

代码语言:javascript
运行
AI代码解释
复制
try { await hubConnection.StopAsync(); }
  finally
  {
    await hubConnection.DisposeAsync();
  }

the the the

调用

不再阻塞,连接将正常关闭。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64449506

复制
相关文章
phpStudy 创建多个站点,绑定域名
站长前面推荐过《phpStudy:非常棒的PHP环境搭建包(支持Apache、IIS、Nginx和LightTPD) 》,今天来简单介绍一下使用 phpStudy 创建多个站点,绑定域名的方法。
星哥玩云
2022/08/13
5K0
phpStudy 创建多个站点,绑定域名
【云上实践】腾讯云服务器如何创建镜像?
待镜像完成创建后,在镜像列表中选择您创建的镜像,单击其所在行右侧的创建实例,即可购买与之前相同镜像的服务器。如下图所示:
Kami米雅
2021/11/08
24.6K0
【云上实践】腾讯云服务器如何创建镜像?
如何使用 Apache Web 服务器配置多个站点
正如我之前的文章中提到的,Apache 的所有配置文件都位于 /etc/httpd/conf 和 /etc/httpd/conf.d。默认情况下,站点的数据位于 /var/www 中。对于多个站点,你需要提供多个位置,每个位置对应托管的站点。
用户8989785
2021/09/09
2.5K0
如何使用 Apache Web 服务器配置多个站点
在我的上一篇文章中,我解释了如何为单个站点配置 Apache Web 服务器,事实证明这很容易。在这篇文章中,我将向你展示如何使用单个 Apache 实例来服务多个站点。
星哥玩云
2022/07/20
2.5K0
腾讯云服务器部署CentOS系统站点
我们使用 oneinstack 一键安装包进行安装,oneinstack的官网地址是:https://oneinstack.com/ 您可以在上面获取更多安装信息。
用户2416682
2019/07/23
17.7K0
腾讯云服务器部署CentOS系统站点
Apache服务器配置多个站点
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106426.html原文链接:https://javaforall.cn
全栈程序员站长
2022/08/04
2.6K0
腾讯云服务器如何创建网站快照?
快照是云服务商提供的一种数据备份方式,用于日常的云硬盘数据备份和恢复。可以简单理解为在线数据的实时副本,在该盘在出现问题时,可以快速恢复到未出问题前的状态。
主机优惠教程
2022/05/21
8.9K0
在一台Apache服务器上创建多个站点(不同域名)
使用不同的域名来区分不同的网站,所有的域名解析都指向同一个 IP 地址。Apache通过在HTTP头中附带的 host参数来判断用户需要访问哪一个网站。
星哥玩云
2022/07/14
3.4K0
一个服务器多个站点如何都启用redis?
2.此插件在服务器上得文件目录为/wp-content/plugins/redis-cache/
季春二九
2023/07/14
4930
腾讯云服务器搭建 WordPress站点『图文教程』
WordPress 是一款常用的搭建个人博客网站软件,该软件使用 PHP 语言开发。您可通过在腾讯云服务器的简单操作部署 WordPress,发布个人博客。
用户2416682
2019/07/23
8.4K1
Apache Web 服务器配置多个站点
正如我之前的文章中提到的,Apache 的所有配置文件都位于 /etc/httpd/conf 和 /etc/httpd/conf.d。默认情况下,站点的数据位于 /var/www 中。对于多个站点,你需要提供多个位置,每个位置对应托管的站点。
用户8989785
2021/09/10
3.4K0
Nginx服务器设置多个站点教程
  把2个站点 example1.com, example2.com 放到 nginx 可以访问的目录 /www/给每个站点分别创建一个 nginx 配置文件 example1.com.conf,example2.com.conf, 并把配置文件放到 /etc/nginx/vhosts/然后在 /etc/nginx.conf 里面加一句 include 把步骤2创建的配置文件全部包含进来(用 * 号)重启 nginx。下面是具体的配置过程:
会长君
2023/04/25
2.2K0
『图文教程』腾讯云服务器搭建 WordPress站点
WordPress 是一款常用的搭建个人博客网站软件,该软件使用 PHP 语言开发。您可通过在腾讯云服务器的简单操作部署 WordPress,发布个人博客。
用户6628475
2019/11/04
3.6K0
『图文教程』腾讯云服务器搭建 WordPress站点
腾讯云服务器搭建 WordPress站点『图文教程』
WordPress 是一款常用的搭建个人博客网站软件,该软件使用 PHP 语言开发。您可通过在腾讯云服务器的简单操作部署 WordPress,发布个人博客。
用户6559734
2019/10/26
4.5K0
腾讯云服务器搭建 WordPress站点『图文教程』
如何将 Discuz! Q 站点接入腾讯云 CDN ,加速站点访问
已成功 安装部署 Discuz! Q 站点 (opens new window)。
佛系豪豪吖
2023/02/23
6.4K0
如何将 Discuz! Q 站点接入腾讯云 CDN ,加速站点访问
如何使用腾讯云轻量服务器手动创建快照
关于如何使用腾讯云轻量服务器手动创建快照,当你对虚拟机进行过一通操作之后,可以让你的虚拟机回滚到创建快照时的状态。也就是是无论你对你的电脑系统进行过任何操作,都可以通过快照让你的系统恢复到之前的状态。
主机优惠教程
2022/05/21
6.1K0
腾讯云服务器创建swap空间
腾讯云服务器安装系统的时候,并没有挂载swap分区。但是我想对数据盘进行分区的时候,发现默认把整个磁盘都用上了,并没用预留空间。swap分区是行不通了,只能试试swap文件。
用户6248011
2019/10/26
11.4K0
腾讯云服务器配置环境部署站点【小白教程】
我这里选择的是 WindowsServer2012 + Tomcat + MySQL操作系统、这个系统可以直接部署我们需要的网页服务,不需要另外安装与配置环境变量了 更适合小白,如果不是可以重装系统,在服务市场选择这款系统,目前免费的
用户2416682
2019/12/12
5.8K0
腾讯云服务器配置环境部署站点【小白教程】
腾讯云服务器创建快照备份教程
腾讯云服务器快照怎么使用?如何创建?快照是指某一时刻云硬盘数据的备份,快照是在云硬盘控制台创建的,腾讯云百科来详细说下手动创建快照的方法:
新手站长
2023/02/25
13.5K0
腾讯云服务器创建快照备份教程
[技巧]腾讯云服务器绑定多个IP(无需任何费用)
这里为什么要分配内网IP呢,因为你在服务器里虽然可以手动配置成任何IP,但是绑定公网IP还是要在控制台操作的,所以需要事先分配好内网IP
繁花云
2018/07/31
27.8K0
[技巧]腾讯云服务器绑定多个IP(无需任何费用)

相似问题

腾讯云服务器?

5554

我在多个腾讯云账号,多个域 名,多个云,如何备案?

3606

如何购买腾讯云服务器?

1426

腾讯云服务器redis?

1134

腾讯云 云服务器转移账号?

24.3K
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文