ASP.NET Core2.0使用SignalR和Redis进行实时信息推送

最终效果


在这里插入图片描述

在这里插入图片描述
下面我只讲解核心代码,因为实现上面效果,还需要很多细节,看完的小伙伴如果有疑问,可以留言。

准备工作和预备知识


首先先用NuGet包下载SignalR包:

Microsoft.AspNetCore.SignalR
StackExchange.Redis

安装过后,需要得就是SignalR的JS文件,这里可以采用npm命令进行安装

npm install @aspnet/signalr

在这里插入图片描述
这样,在路径下面就会出现两个文件,一个是node_modules文件夹和package-lock.json文件

在这里插入图片描述
我们点进node_modules文件夹的时候,发现有很多文件,但我们只需要一个JS文件就够了
在这里插入图片描述
然后把这个js文件导入到我们的项目里面,在ASP.NET Core的Startup文件里的ConfigureServices方法添加一段代码

services.AddSignalR()

在这里插入图片描述
然后在Configure方法里也添加一段代码

            app.UseSignalR(route =>
            {
                route.MapHub<InfoHub>("/hubs");
            });

在这里插入图片描述
到这里,准备工作就完成了,讲讲预备知识。
在发送信息和接收信息的时候,需要实现的是实时性,即一旦发送人发送了信息,收件人能够立刻收到消息并显示,典型的案例就是聊天室,而实时性又怎么实现呢?大体有三种方式

1.AJAX轮询

此方法是客户端隔一段时间便向服务端请求,询问服务端有没有消息,因为请求大多数是没用的,所以会消耗比较多的资源,

2.长轮询(long polling)

此方法和轮询差不多,不过和轮询的区别在于,发送请求到服务端的时候,服务器端“阻塞”,直到有新消息可以返回的时候,服务端来回响应这个请求,因为请求到服务器,服务端会"阻塞",所以也会消耗一定的资源。

3.WebSocket

WebSocket是HTML5推出的新协议,上面的两种方式中,服务端扮演的被动的角色,即服务端不会主动的告诉客户端有新的消息,而WebSocket解决了这个问题,它实现了双向通讯,服务端可以主动的告诉客户端有消息。具体关于WebSocket可以看看下面的文章。
https://www.zhihu.com/question/20215561

而我们SignalR实现实时性依靠的也是WebSocket,当浏览器支持WebSocket的时候,SignalR就会使用WebSocket来进行交互,如果不支持,则会使用其它方式来实现实时性,即WebSocket在SignalR中是第一优先选择。
到这里,我们准备工作和预备知识就讲完了,下面来看看实现信息推送的流程

简单来说,信息推送的主要思路就是    发    存     读

实现基础:
    和    操作需要实时性,而SignalR可以实现实时性。*
    操作我采用Redis进行存储,关于Redis的讲解,可以参考下面的文章。
https://www.cnblogs.com/yuhangwang/p/5817930.html
采用Redis的原因有两个:
1.读写快速
因为信息是访问频繁而数据量又大的一个东西,如果存储在关系数据库的确可以,但是频繁的取读关系数据库,显得有点浪费性能,而Redis数据库是基于内存的数据库,在读写方面,有着很大的优势
2.Redis的List类型适合存储消息
Redis支持很多类型,String,SortList,List等,其中List类型比较适合我们存放消息。
在这里插入图片描述

在这里插入图片描述
下面来看看怎么用代码来实现功能。

首先我们要在项目里面创造一个继承于Hub类的继承类(或者说创造一个集线器),它有什么用呢?他减弱了客户端和服务端两个独立物理环境的限界,使得客户端可以调用服务端的方法,服务端可以调用客户端的方法,不过方法只限于集线器中的方法。有关Hub的信息可以看这篇文章
https://www.cnblogs.com/hnsongbiao/p/8716722.html
在这里插入图片描述
重写OnConnectedAsync方法,此方法会在建立WebSocket时调用。
在这里插入图片描述

  public override async  Task OnConnectedAsync()
  {     //获取连接ID 
        string id = Context.ConnectionId;
        
        //这里获取用户唯一标识方法不唯一,我这里是通过依赖注入拿到IHttpContextAccessor接口的实现类
        //HttpContextAccessor然后通过Session取出用户唯一标识
        string Name = _httpContextAccessor.HttpContext.Session.GetString("UserName");
           if (Name == null)
           {
                  //只写了简单处理,到时候要用异常处理             
                  return;
            }
            
           //获取用户
           Admin admin = _adminservice.GetAdmin(Name);
           if (admin == null)
          {
                 //只写了简单处理,到时候要用异常处理
                 return;
          }
          
          //将连接用户的ConnectionID和UserID用UserInfo类存储
          UserInfo info = new UserInfo
          {
                  NickName=admin.Name,
                   UserID = admin.ID,
                   ConnectionID = id
          }
                     
          //将用户信息存进Redis
         await _RedisService.SetStringAsync("UserInfo_" + admin.ID, JsonConvert.SerializeObject(info));
         await base.OnConnectedAsync();
                     
  
  }

然后自定义一个发送方法SendMessage给客户端调用

  public async Task SendMessage(string fromNickName,string Name,string Message)
  {
            //收件人的信息
            Admin admin = _adminservice.GetAdmin(Name);
            //发送人的信息
            Admin fromAdmin = _adminservice.GetAdmin(fromNickName);
            
              if (admin == null||fromAdmin==null)
              {
                    //只写了简单处理,到时候要用异常处理
                   return;
              }
            //获取Redis里面用户的连接ID和UserID
            string info = await _RedisService.GetStringAsync("UserInfo_" + admin.ID);
            
             if (info == null)
             {
                  //只写了简单处理,到时候要用异常处理
                   return;
             }
             UserInfo UserInfo =JsonConvert.DeserializeObject<UserInfo>(info);
             
             //对收件人发送信息,调用客户端的ReceiveMessage方法
             await Clients.Client(UserInfo.ConnectionID).SendAsync("ReceiveMessage");
             
            //包装要发送的信息
            Info message = new Info()
            {
                 FromNickName = fromNickName,
                 FromUserID = fromAdmin.ID,
                 FromUserMessage = Message,
                 CreateTime = DateTime.Now.ToShortDateString(),
                 status = 0
            }
            
    //将消息加入到Redis
    await _RedisService.InfoLeftPushAsync("Message_" + admin.ID, JsonConvert.SerializeObject(message));
}

上面代码中,出现了RedisServiceUserInfoInfo类,RedisService是Redis提供存储,读取服务,UserInfo用来存储用户连接的信息,Info用来存储消息文本

UserInfo类

 public class UserInfo
 {
        public string NickName { get; set; }
        
        public string UserID { get; set; }
        
        public string ConnectionID { get; set; }
        
 }

Info类

 public class Info
 {
      public string FromUserID { get; set; }
      
      public string FromUserMessage { get; set; }
      
      public string CreateTime { get; set; }
      
       public string FromNickName { get; set; }
       
        public int status { get; set; }
      
 }

IRedisService:这里因为我对Redis的封装比较差,就不误人子弟了,这里只提供了接口,具体实现小伙伴可以通过网上其他人对StackExchange.Redis的描述来实现功能。

   public interface IRedisService
   {
        /// <summary>
        /// 设置String类型
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        Task<bool> SetStringAsync(string key, string value);
          
         /// <summary>
        /// 获取String键
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<string> GetStringAsync(string key);
         
         /// <summary>
        /// 返回全部信息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<List<Info>> GetInfoAsync(string key);

         /// <summary>
        /// 插入消息进Redis
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        Task InfoLeftPushAsync(string key, string value);
         
          /// <summary>
        /// 获取未读消息数量
        /// </summary>
        /// <param name="key">用户id</param>
        /// <returns></returns>
        Task<int> GetUnreadInfoCountAsync(string key);

         /// <summary>
        /// 获取未读消息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<List<Info>> GetUnreadInfoAsync(string key);
         
         /// <summary>
        /// 获取已读消息
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<List<Info>> GetReadInfoAsync(string key);
      
   }
   

到这里Hub类就完成了,剩下的就是客户端的js代码 ,首先是收件人的核心代码

<script>
    //创造映射路径的连接
    var connection = new signalR.HubConnectionBuilder().withUrl("/hubs").build();
    
      //Receive方法,其作用类似监听服务端是否使用了ReceiveMessage,如果使用了,则使
      //通知灯右上角的数字加1
      connection.on("ReceiveMessage", function () {
              var count = infoCount.text();
               if (count == "") {
                   count = 0;
               }
                console.log(count);
                infoCount.text(++count);
        });
        
        //开始连接
         connection.start().catch(function (res) {
                 alert("连接错误");
         });
         
</script>

发送人发送的核心代码

<script>
      //创造映射路径的连接
      var connection = new signalR.HubConnectionBuilder().withUrl("/hubs").build();
 
     $("#Send").click(function () {
         var Name = $("#Name").val();
         var Message = $("#message").val();
          connection.invoke("SendMessage","@User.Identity.Name", Name, Message);
     });
        //开始连接
         connection.start().catch(function (res) {
                  alert("连接错误");
         });          

  </script>

到这里所有代码都写完了,上面的所有代码只是核心代码,还有好多细节代码需要小伙伴实现,这里就不一一给出了,有什么疑问的小伙伴欢迎留言。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值