先从理论上解释一下二者的区别。前端
大多数传统的web应用是这样的:客户端发起http请求到服务端,服务端返回对应的结果。像这样:git
也就是说,传统的web应用都是客户端主动发起请求到服务端。web
那么实时web应用呢?它不须要主动发起请求,服务端能够主动推送信息到客户端。后端
举栗子的话,实时聊天工具、web游戏等均可以算是实时应用。浏览器
若是想作一个实时应用,最好用web socket。很早之前我也写过web socket的实现方式,但不够全面,这里再补上一篇。服务器
来讲说signalR,它是一款开源的实时框架,可使用三种方式实现通讯(long polling、server sent events、web socket)。它很好的整合了底层技术,让咱们能够不用关注底层技术实现而把精力聚焦在业务实现上。一个完整的signalR包括客户端和服务端,服务端支持net core/net framework,还支持大部分客户端,好比浏览器和桌面应用。app
为了兼容不一样浏览器(客户端)和服务端,signalR采用了回落机制,使得它能够根据状况协商使用不一样的底层传输方式。假如浏览器不支持web socket,就自动降级使用sse,再不行就long polling。固然,也能够禁用这种机制,指定其中一种。框架
长轮询是客户端发起请求到服务端,服务器有数据就会直接返回。若是没有数据就保持链接而且等待,一直到有新的数据返回。若是请求保持到一段时间仍然没有返回,这时候就会超时,而后客户端再次发起请求。socket
这种方式优势就是简单,缺点就是资源消耗太多,基本是不考虑的。async
若是使用了sse,服务器就拥有了向客户端推送的能力,这些信息和流信息差很少,期间会保持链接。
这种方式优势仍是简单,也支持自动重连,综合来说比long polling好用。缺点也很明显,不支持旧的浏览器不说,还只能发送本文信息,并且浏览器对sse还有链接数量的限制(6个)。
web socket容许客户端和服务端同时向对方发送消息(也就是双工通讯),并且不限制信息类型。虽然浏览器一样有链接数量限制(多是50个),但比sse强得多。理论上最优先使用。
开始以前,还须要了解RPC和Hub的概念。
RPC:全程Remote Procedure Call,字面意思远程服务调用,能够像调用本地方法同样调用远程服务。前端能够调用后端方法,后端也能够调用前端方法。
Hub:基于RPC,接受从客户端发过来的消息,也同时负责把服务端的消息发送给客户端。客户端能够调用Hub里面的方法,服务端能够经过Hub调用客户端里面的方法。
好了,概念已经理解清楚了,接下来上代码。
在项目里新增Hub类:
using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRDemo.Server { public class SignalRHub : Hub { /// <summary> /// 客户链接成功时触发 /// </summary> /// <returns></returns> public override async Task OnConnectedAsync() { var cid = Context.ConnectionId; //根据id获取指定客户端 var client = Clients.Client(cid); //向指定用户发送消息 await client.SendAsync("Self", cid); //像全部用户发送消息 await Clients.All.SendAsync("AddMsg", $"{cid}加入了聊天室"); } } }
为了让外部能够访问,咱们还须要一个控制器。在控制器里声明随便建一个:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; using SignalRDemo.Server; using System.Threading.Tasks; namespace SignalRDemo.Controllers { public class HomeController : Controller { private readonly IHubContext<SignalRHub> _countHub; public HomeController(IHubContext<SignalRHub> countHub) { _countHub = countHub; } /// <summary> /// 发送信息 /// </summary> /// <param name="msg"></param> /// <param name="id"></param> /// <returns></returns> public async Task Send(string msg, string id) { await _countHub.Clients.All.SendAsync("AddMsg", $"{id}:{msg}"); } } }
再而后进入StartUp设置端点:
endpoints.MapHub<SignalRHub>("/hub");
完成之后,配置signalr客户端:
setupConn = () => { conn = new signalR.HubConnectionBuilder() .withUrl("/hub") .build(); conn.on("AddMsg", (obj) => { $('#msgPanel').append(`<p>${obj}</p>`); }); conn.on("Finished", () => { conn.stop(); $('#msgPanel').text('log out!'); }); conn.on("Self", (obj) => { $('#userId').text(obj); }); conn.start() .catch(err => console.log(err)); }
要注意withUrl里面的路径就是以前设置好的端点。
运行效果:
Hub还支持组操做,好比:
//将用户添加到A组
await Groups.AddToGroupAsync(Context.ConnectionId, "GroupA");
//将用户踢出A组 await Groups.RemoveFromGroupAsync(Context.ConnectionId, "GroupA");
//向A组全部成员广播消息 await Clients.Group("GroupA").SendAsync("AddMsg", "群组消息");
更多操做请参考官方文档。
本文演示demo的源码见git,地址:https://gitee.com/muchengqingxin/SignalRDemo.git