一、SignalR 概述SignalR是微软为实现实时通信的一个类库。一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。
signalR内部有两类对象:
- 持久连接
一个持久连接代表了一个端点,它可以发送单一接收者,Group接受者或者广播信息。持久连接的api是SignalR提供给开发者进入低级别协议的api。连接模型使用起来和WCF比较类似。
- Hubs(集线器)
Hubs是SignalR提供的高级别的api,它允许客户端和服务端,在自己这边相互调用对方的方法。Hubs模型类似于.Net Remoting。使用Hubs也可以让你传递强类型参数,进行模型绑定。
SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。
SignalR 和 WebSocket如果客户端和服务器都支持WebSocket,那么SignalR会通过WebSocket来传输数据。当然你也可以自己使用WebSocket来实现SignalR的功能,不过使用SignalR你就不用考虑如果客户端或者服务器不支持WebSocket的问题了。
二、SignalR的协议选择SignalR是可以在客户端和服务器端进行即时通讯的几种协议的抽象和实现。一个SignalR连接是通过http请求发起的,然后上升为WebSocket(如果客户端和服务端都支持)。WebSocket是SignalR最理想的协议,它可以有效地利用服务器端的内存,有着最低的延迟,最多的基础特性(比如客户端和服务端的全双工连接),不过它也有着严格的要求,需要服务器端使用Windows Server 2012或者Windows 8以上的系统,也需要.NET Framework 4.5.。如果不符合这些要求,那么SignalR会使用其他的协议来建立连接。
HTML 5协议
・WebSocket。如果服务器和客户端都支持,那么就使用WebSocket协议来进行通讯。
・服务器推送事件(Server-sent Events)。除了IE,其他的浏览器基本都支持。
Comet协议
・Forever Frame (只支持IE)。
・Ajax长轮询(Ajax long polling)。
SignalR协议选择过程
1.如果浏览器是IE8或者更早的版本,使用长轮询。
2.如果配置了Jsonp(如果连接开始的时候jsonp的参数设置为true), 使用长轮询。
3.如果是跨域连接, 如果下面的条件符合就会使用WebSocket,如果有条件不符合,那就是用长轮询。
・客户端支持跨域连接
・客户端支持WebSocket
・服务器端支持WebSocket
4.如果没有配置jsonp,而且不是跨域连接,如果客户端和服务端都支持WebSocket,那么就使用WebSocket。
5.如果客户端或者服务端不支持WebSocket,使用服务器推送事件。
6.如果不支持服务器推送事件,使用Forever Frame。
7.如果不支持Forever Frame,使用长轮询。
监控协议可以通过在你的Hub上开启logging来监控你的SignalR使用了什么协议。
$.connection.hub.logging = true;
指定协议SignalR判断协议也需要消耗一定的客户端、服务端资源,如果你清楚客户端、服务端支持的协议,那么你可以指定使用某种协议来建立连接。
比如,你知道客户端只支持长轮询,那么你可以指定使用长轮询来进行通讯。
connection.start({ transport: 'longPolling' });
你也可以指定一个序列,客户端会按照序列里的顺序来进行通讯。下面的代码的作用是,先使用WebSocket,如果失败了,就使用长轮询。
connection.start({ transport: ['webSockets','longPolling'] });
SignalR包含下面四种指定的协议常量
・webSockets
・foreverFrame
・serverSentEvents
・longPolling
三、SignalR的三种实现方式1. 集线器类(Hub) + 非自动生成代理模式
服务端与客户端分别定义的相对应的方法,客户端通过代理对象调用服务端的方法,服务端通过IHubConnectionContext回调客户端的方法,客户端通过回调方法接收结果。
JS端调用服务端方法采用:chat.invoke,而被服务端回调的方法则采用:chat.on (这里的chat是createHubProxy创建得来的)
var conn = $.hubConnection();
conn.qs = { "clientName": clientName };
conn.start().done(function () {
$("#btnSend").click(function () {
var toUserId = eUsers.val();
if (toUserId != "") {
chat.invoke("sendOne", toUserId, $("#message").val())
.done(function () {
//alert("发送成功!");
$("#message").val("").focus();
})
.fail(function (e) {
alert(e);
$("#message").focus();
});
}
else {
chat.invoke("send", $("#message").val())
.done(function () {
//alert("发送成功!");
$("#message").val("").focus();
})
.fail(function (e) {
alert(e);
$("#message").focus();
});
}
});
});
var chat = conn.createHubProxy("chat");
chat.on("receiveMessage", function (dt, cn, msg) {
var clsName = "linfo";
if (cn == clientName || cn.indexOf("您对") >= 0) clsName = "rinfo";
eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 说:<br/>" + msg + "</p>");
eChatBox.scrollTop(eChatBox[0].scrollHeight);
});
chat.on("userChange", function (dt, msg, users) {
eChatBox.append("<p>" + dt + " " + msg + "</p>");
eUsers.find("option[value!='']").remove();
for (var i = 0; i < users.length; i++) {
if (users[i].Value == clientName) continue;
eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</option>")
}
});
2. 集线器类(Hub)+ 自动生成代理模式
需要js引用
<script src="~/signalr/hubs" type="text/javascript"></script>
然而,我们在写代码的时候上面的引用并不存在,而当运行后就会自动生成上述signalr的代理脚本
这就是与非自动生成代理脚本最根本的区别,也正是因为这个自动生成的脚本,我们可以在JS中更加方便的调用服务端方法及定义回调方法,调用服务端方法采用:chat.server.XXX,而被服务端回调的客户端方法则采用:chat.client.XXX
3.持久化连接类(PersistentConnection)
・
Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection")
・
需实现继承自PersistentConnection类的自定义的持久化连接类,在这个连接中可以重写:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同时有几个重要的属性成员Connection、Groups,服务端发消息给客户端采用:Connection.Broadcast(广播,所有客户端都可以收到消息),Connection.Send(发送给指定的客户端)
具体实现参考
四、使用RignalR实现新消息推送(集线器类(Hub)+ 自动生成代理模式
)1.app.MapSignalR();
using System.Data.Entity;
using Microsoft.Owin;
using Owin;
using RCRS.WebApp.Town.Migrations;
using RCRS.WebApp.Town.Models.DomainEntity;
[assembly: OwinStartupAttribute(typeof(RCRS.WebApp.Town.Startup))]
namespace RCRS.WebApp.Town
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
Database.SetInitializer(new MigrateDatabaseToLatestVersion<TownContext, TownConfiguration>());
}
}
}
2. NotificationHub
using System.Linq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using RCRS.WebApp.Town.Models.Town;
namespace RCRS.WebApp.Town.Hubs
{
[HubName("NotificationHub")]
public class NotificationHub : Hub
{
public void Connect(string userId)
{
var id = Context.ConnectionId;
if (BizHub.ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
{
BizHub.ConnectedUsers.Add(new HubUser { ConnectionId = id, UserId = userId });
// send to caller
Clients.Caller.onConnected(id, userId, BizHub.ConnectedUsers);
// send to all except caller client
Clients.AllExcept(id).onNewUserConnected(id, userId);
}
}
public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
var item = BizHub.ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
BizHub.ConnectedUsers.Remove(item);
var id = Context.ConnectionId;
Clients.All.onUserDisconnected(id, item.UserId);
}
return base.OnDisconnected(stopCalled);
}
}
}
3.BizHub
/// <summary> </summary>
public static List<HubUser> ConnectedUsers = new List<HubUser>();
public void NotifyAll(string msg)
{
var hub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
hub.Clients.All.broadcaastNotif(msg);
}
public void NotifyPrivate(string toUserId, string msg)
{
var toUser = ConnectedUsers.FirstOrDefault(x => x.UserId == toUserId);
var hub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
if (toUser != null)
{
// send to
hub.Clients.Client(toUser.ConnectionId).broadcaastNotif(msg);
}
}
public void NotifyRole(List<string> roleLs, string msg)
{
List<string> lsUserIds = new List<string>();
using (ApplicationDbContext context = new ApplicationDbContext())
{
string cmd = getUsersByRoleLs(roleLs);
lsUserIds = context.Database.SqlQuery<string>(cmd).ToListAsync().Result;
}
foreach (string toUserId in lsUserIds)
NotifyPrivate(toUserId, msg);
}
4.引用js
bundles.Add(new ScriptBundle("~/bundles/signalR").Include(
"~/Scripts/jquery.signalR-2.2.3.js"));
5.
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var id = '@ViewBag.UserId';
var notifyHub = $.connection.NotificationHub;
notifyHub.client.broadcaastNotif = function (message) {
$("#assist-top-new-msg").text(message);
$("#assist-msg-list-new-flg").text(message);
};
$.connection.hub.start()
.done(function () {
console.log("Notification Hub Connected!");
//Server Call
notifyHub.server.connect(id);
})
.fail(function () {
console.log("Could not Connect Notification Hub!");
});
});
</script>
posted on 2018-05-23 15:02
Ying-er 阅读(1373)
评论(0) 编辑 收藏 所属分类:
.Net 、
喵