下面我只讲解核心代码,因为实现上面效果,还需要很多细节,看完的小伙伴如果有疑问,可以留言。
首先先用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()
app.UseSignalR(route =>
{
route.MapHub("/hubs");
});
到这里,准备工作就完成了,讲讲预备知识。
在发送信息和接收信息的时候,需要实现的是实时性,即一旦发送人发送了信息,收件人能够立刻收到消息并显示,典型的案例就是聊天室,而实时性又怎么实现呢?大体有三种方式
此方法是客户端隔一段时间便向服务端请求,询问服务端有没有消息,因为请求大多数是没用的,所以会消耗比较多的资源,
此方法和轮询差不多,不过和轮询的区别在于,发送请求到服务端的时候,服务器端“阻塞”,直到有新消息可以返回的时候,服务端来回响应这个请求,因为请求到服务器,服务端会"阻塞",所以也会消耗一定的资源。
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(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));
}
上面代码中,出现了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代码 ,首先是收件人的核心代码
发送人发送的核心代码
到这里所有代码都写完了,上面的所有代码只是核心代码,还有好多细节代码需要小伙伴实现,这里就不一一给出了,有什么疑问的小伙伴欢迎留言。