作者:汜水 | 来源:互联网 | 2023-09-15 13:54
最终效果
下面我只讲解核心代码,因为实现上面效果,还需要很多细节,看完的小伙伴如果有疑问,可以留言。
准备工作和预备知识
首先先用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("/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}//将用户信息存进Redisawait _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和UserIDstring info = await _RedisService.GetStringAsync("UserInfo_" + admin.ID);if (info == null){//只写了简单处理,到时候要用异常处理return;}UserInfo UserInfo =JsonConvert.DeserializeObject(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}//将消息加入到Redisawait _RedisService.InfoLeftPushAsync("Message_" + admin.ID, JsonConvert.SerializeObject(message));
}
上面代码中,出现了RedisService和UserInfo和Info类,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{/// /// 设置String类型/// /// /// /// Task SetStringAsync(string key, string value);/// /// 获取String键/// /// /// Task GetStringAsync(string key);/// /// 返回全部信息/// /// /// Task> GetInfoAsync(string key);/// /// 插入消息进Redis/// /// 键/// 值/// Task InfoLeftPushAsync(string key, string value);/// /// 获取未读消息数量/// /// 用户id/// Task GetUnreadInfoCountAsync(string key);/// /// 获取未读消息/// /// /// Task> GetUnreadInfoAsync(string key);/// /// 获取已读消息/// /// /// Task> GetReadInfoAsync(string key);}
到这里Hub类就完成了,剩下的就是客户端的js代码 ,首先是收件人的核心代码
发送人发送的核心代码
到这里所有代码都写完了,上面的所有代码只是核心代码,还有好多细节代码需要小伙伴实现,这里就不一一给出了,有什么疑问的小伙伴欢迎留言。