SignalR---服务端
前段时间把SignalR的官网教程大致看了一下,算是翻译了一遍,加上了自己的个人理解,
一下上传三个文件,分别是服务端、web客户端、DOTNET客户端相关文档,供大家参考。
其中有一处由于翻译时不专心,但又没能找到的错误,是说$.connetion.myHub和$.hubConnection其中是同一个对象的。
下面以代码形式上传,篇幅过长,有翻译不当的地方请浏览指出。
在Startup类中的Configuration方法里注册hub集线器。
任何连接或集线器连接和配置都在这里注册。 app.MapSignalr();注册所有的集线器。 配置signalr代理的目录 自动生成的js集线器代理默认生成在路径为web项目根目录/signalr/hubs。
是可以通过代码改变这个默认路径的。
在服务端
appMapSignalR("/signalr",new HubConfiguration());其中signalr为新的路径。 在JavaScript客户端(生成代理)
$.connection.hub.url="/signalr";
$.connection.hub.start().done(init); 在JavaScript客户端(不生成代理)
var connection=$.connection("/signalr",{userDefaultPath:false}); 在.NET客户端
var hubConnection=new HubConnection("http://contoso.com/signalr",useDefaulrUrl:false); 创建hub服务器
public class MyHub:Hub 要继承Hub这个父类才能成为集线器。
在JavaScript客户端使用生成的代理
var connectionProxy=$.connection.myHub;其中muHub为服务器创建的Hub。
在服务端可以改变Hub的名字,[HubName(proxyHub)]。 定义强类型的Hubs
通过定义一个接口interface让你的客户端调用,从Hub<T>派生。
public class StrongHub:Hub<IClient>
{
public void Send(string message)
{
Clients.All.MewMessage(message);
} public interface IClinet
{
void NewMessage(string message);
}
} 如何在服务端定义一个方法让客户端调用
只要在Hub类里把方法定义成public公共的方法就行了。 Js客户端调用服务端的方法时是驼峰命名法(首字母小写)
服务端的方法
public void NewContosoChatMessage(string userName, string message);
客户端调用时
contosoChatHubProxy.server.newContosoChatMessage(userName, message);
首字母小写是可以调到的。 如果想要一个具体的名字(原样输出),则使用到HubName这个属性。
这样就可以原样调用。
服务端
[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)
客户端
contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message); 在服务端也可以使用异步方法
服务端
public async Task<IEnumerable<Stock>> GetAllStocks()
{
// Returns data from a web service.
var uri = Util.getServiceUri("Stocks");
using (HttpClient httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri);
return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
}
}
客户端
stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
$.each(stocks, function () {
alert(this.Symbol + ' ' + this.Price);
});
}); Hub集线器提供了一个进程参数,通过IProgress<T>来定义
public class ProgressHub : Hub
{
public async Task<string> DoLongRunningThing(IProgress<int> progress)
{
for (int i = 0; i <= 100; i+=5)
{
await Task.Delay(200);
progress.Report(i);
}
return "Job complete!";
}
} 服务端调用客户端的方法
使用Clients这个属性去调用客户端的方法(这个方法必须是在连接了服务器的克短短里)。
服务端
public class ContosoChatHub : Hub
{
public void NewContosoChatMessage(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
客户端
contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
$('#discussion').append('<li><strong>' + htmlEncode(name)
+ '</strong>: ' + htmlEncode(message) + '<li>');
}; 不能从客服端方法里得到返回值。int x = Clients.All.add(1,1);这样的代码就不起作用。 客户端可以定义复杂的参数类型
服务端
public void SendMessage(string name, string message)
{
Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}
客户端
var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
console.log(message.UserName + ' ' + message.Message);
}); Clients返回的是HubConnectionContext集线器连接上下文对象。
可以通过这个对象的属性选择那些客户端接收PRC。 Clients.All.addContosoChatMessageToPage(name, message);全部连接的客户端 Clients.Caller.addContosoChatMessageToPage(name, message);调用者 Clients.Others.addContosoChatMessageToPage(name, message);全部的连接者,出来调用者 Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);特定的ConnectionId Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);//全部的连接者,除了特定的connectionId Clients.Group(groupName).addContosoChatMessageToPage(name, message);在一个特定的组里的全部连接者 Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);在一个特定组里的全部连接者,除了特定从conectionId Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);在一个组里的全部连接者,除了调用者 Clients.User(userid).addContosoChatMessageToPage(name, message);一个特定的用户,用userId标识 Clients.Clients(ConnectionIds).broadcastMessage(name, message);在一个列表里的所有客户端和群组,ConnectioIds是一个列表 Clients.Groups(GroupIds).broadcastMessage(name, message);一个组列表 Clients.Client(username).broadcastMessage(name, message);特定的用户名 方法名称的匹配不区分大小写 Clients.All.addContosoChatMessageToPage在服务器上将会执行AddContosoChatMessageToPage,
addcontosochatmessagetopage或addContosoChatMessageToPage在客户端上。 使用字符串变量当作方法名
public void NewContosoChatMessage(string name, string message)
{
string methodToCall = "addContosoChatMessageToPage";
IClientProxy proxy = Clients.All;
proxy.Invoke(methodToCall, name, message);
}
methodToCall为字符串,
定义一个IClientPeoxy类,定义接受者。
通过IClientProxy的Invoke方法来实现字符串方法,其中methodToCall为方法名,name和message为参数。 在hub集线器如何管理组成员
SignalR的群组(Groups)提供了广播消息的方法对所有的组成员。
一个组可以有任意数量的成员,一个成员也可以在很多的组里。
可以添加和删除组成员。
服务端
public class ContosoChatHub : Hub
{
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
} public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
}
Js客户端
contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName); 组会自动创建,也会最后一个成员退出后自动消失。 添加一个客户端到群组并发送消息
public async Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
} 组成员持久化
Signalr是持久的,用户不是。 在Hub集线器如何管理连接的生命周期
可以重写OnConnected、OnDisconnected、OnReconnected集线器的虚方法跟踪用户的连接和断开。
跟踪用户名和连接ID之间的关联,这样就当客户端连接或断开时就可以运行自己的代码。
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
} public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
} public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
} 调用OnConnected、OnDisconnected和OnReconnected方法
每次浏览器导航到新页面时,都必须建立一个新连接,这意味着SignalR将执行该
OnDisconneted方法和OnConnected方法。当建立新的连接时,SignalR总是创建一个
新的连接ID。 OnReconnected 当连接时候没有超时,重新连接时会别调用。
OnDisconnected 当客户端断开连接并且SignalR无法自动重新连接时,例如浏览器刷新到新页面。 对一个客户端,它们的调用顺序是OnConnected、OnReconnected、OnDisconnected
或者OnConnected、OnDisconnected。
而不可能是OnConnected、OnDisconnected、OnResconnected。 某些情况下,OnDisconnected不会被调用,例如服务端关闭、AppDomain被回收。 一台服务器上线或AppDomain回收,某些客户端将触发OnReconnected事件。 如何从Context属性获取有关客户端的消息
Context属性是一个HubCallerContext类型的对象,通过这个对象可以获取调用者的信息。 string connectionID = Context.ConnectionId;调用者的connectId
connectionID是一个GUID由SignalR生成,不能有自己生成。
每个连接都有一个connectionID,如果应用程序有多个hub集线器,
则所有Hub都使用相同的connectionID。 System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;HTTP标头数据 System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString; System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
string parameterValue = queryString["parametername"];查询字符串数据 可以在客户端定义查询字符串,这也是传递数据的一种方式
$.connection.hub.qs = { "version" : "1.0" }; string transportMethod = queryString["transport"];连接传输方法
值为transportMethod“webSockets”,“serverSentEvents”,“foreverFrame”或“longPolling”。 System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies; Cookie
也可以从Context.RequestCookies得到。 System.Security.Principal.IPrincipal user = Context.User;用户信息 System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();请求的HttpContext对象。
也可以是HttpContext.Current。 如何传递在客户端和hub类中传递数据
客户端代理提供了一个state对象用于存储想要传输的数据。
在服务端可以通过Client.Caller属性获取到这些数据。 Client.Caller属性在两个方向都有效,也可以更新服务端的值传回到客户端。 用方法名携带数据
js客户端
contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";
.NET客户端
contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1"; 服务端
public void NewContosoChatMessage(string data)
{
string userName = Clients.Caller.userName;
string computerName = Clients.Caller.computerName;
Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
} 在强类型hub集线器中,通过Client.CallerState访问这些数据。
public void NewContosoChatMessage(string data)
{
string userName = Clients.CallerState.userName;
string computerName = Clients.CallerState.computerName;
Clients.Others.addContosoChatMessageToPage(data, userName, computerName);
} 记录集线器的错误
public class ErrorHandlingPipelineModule : HubPipelineModule
{
protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext)
{
Debug.WriteLine("=> Exception " + exceptionContext.Error.Message);
if (exceptionContext.Error.InnerException != null)
{
Debug.WriteLine("=> Inner Exception " + exceptionContext.Error.InnerException.Message);
}
base.OnIncomingError(exceptionContext, invokerContext); }
} 该类继承自HubPipelineModule,应为把错误抛给客户端是不安全的,
所以定义个类用户处理这些错误。
这个类重写了OnIncomingError方法来实现错误的处理。 把这个类注册到异常处理模型中
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule());
app.MapSignalR();
} 在SignalR中,使用HubException类来。可以从任何中枢抛出错误。
显示HtbException类的服务器代码
public class MyHub : Hub
{
public void Send(string message)
{
if(message.Contains("<script>"))
{
throw new HubException("This message will flow to the client", new { user = Context.User.Identity.Name, message = message });
} Clients.All.send(message);
}
}
JavaScript客户端代码演示了在集线器中抛出HubException的响应
myHub.server.send("<script>")
.fail(function (e) {
if (e.source === 'HubException') {
console.log(e.message + ' : ' + e.data.user);
}
});
.NET客户端代码演示在集线器中抛出HubException的响应
try
{
await myHub.Invoke("Send", "<script>");
}
catch(HubException ex)
{
Conosle.WriteLine(ex.Message);
} 如何在Hub类外部调用客户端的方法和组管理
要从与Hub类不同的类调用客户端的方法,就要获取Hub集线器的SignalR的context上下文对象。
并使用它来调用客户端上的方法或管理组。
// For the complete example, go to
// http://www.asp.net/signalr/overview/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
// Singleton instance
private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>())); private IHubContext _context; private StockTicker(IHubContext context)
{
_context = context;
} // This method is invoked by a Timer object.
private void UpdateStockPrices(object state)
{
foreach (var stock in _stocks.Values)
{
if (TryUpdateStockPrice(stock))
{
_context.Clients.All.updateStockPrice(stock);
}
}
} 上下文可以指定那些客户端将接受RPC
context.Clients.All.addContosoChatMessageToPage(name, message);所有连接的客户端 context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);由连接标识的特定客户端 context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);所有连接的客户端,处理指定的connectionID Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);指定组中除connectionID外指定客户端之外的所有连接的客户端 管理组成员资格
对于管理组,有与Hub类中相同的选项。
context.Groups.Add(connectionID, groupName);将客户端添加到组
context.Groups.Remove(connectionID, groupName);从组中删除客户端 如何自定义Hubs管道
SignalR可以将自己的代码注入到Hub管道中,以下是自定义集线器管道模块,
用于记录哭护短接受的每个传入方法调用和在客户端上传出的方法。
public class LoggingPipelineModule : HubPipelineModule
{
protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context)
{
Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name);
return base.OnBeforeIncoming(context);
}
protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context)
{
Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub);
return base.OnBeforeOutgoing(context);
}
}
这个类继承自HubPipelineModule,在GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());注册这个模型。
public void Configuration(IAppBuilder app)
{
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
app.MapSignalR();
}
SignalR---服务端的更多相关文章
- SignalR入门之多平台SignalR服务端
之前创建SignalR服务端是基于Web应用程序而言的.那么能不能把SignalR服务端做成控制台应用程序.Winform或windows服务呢? 答案是肯定的. 之前尽管看起来好像是IIS和ASP. ...
- 创建自托管的SignalR服务端
微软官方例子地址:http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host 1.说明: SignalR服务端可 ...
- 记录一次SignalR服务端实现过程
前言:最近手上一个项目需要后端实时推送数据到前端,第一个想到的就是微软的SignalR,由于之前都是平时没事写的Demo,没有用到实际项目中,这次恰好用到了,因此记录下来整个实现过程(网上也有很多类似 ...
- .net core signalR 服务端强制中断用户连接
.net core signalR 服务端断开连接 { } { } *:first-child { } *:last-child { } { } { } { } { } { } { } { } { } ...
- signalr服务端-基础搭建
signalr 支持 iis托管.winform.windowsservices.wpf 托管 这里我采用winfrom托管 首先画一个这样的窗体 在服务项目通过项目管理包安装signalr类库 安装 ...
- WPF创建SignalR服务端(转)
在网上看到了一个帖子,比较详细,博主写的很好. 地址:http://blog.csdn.net/lordwish/article/details/51786200
- 在后台主机中托管SignalR服务并广播心跳包
什么是后台主机 在之前的 Asp.NETCore 轻松学系列中,曾经介绍过一个轻量级服务主机 IHostedService ,利用 IHostedService 可以轻松的实现一个系统级别的后台服务, ...
- 使用SignalR实现服务端消息推送
概述 这篇文章参考的是Server Broadcast with SignalR 2这篇教程,很不错的一篇教程,如果有兴趣的话可以查看原文,今天记录下来作为一个学习笔记,这样今后翻阅会更方便一点. 这 ...
- [SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端
原文:[SignalR]SignalR与WCF双工模式结合实现服务端数据直推浏览器端 之前开发基于WinForm监控的软件,服务端基于Wcf实现,里面涉及双工模式,在客户端里面,采用心跳包机制保持与服 ...
- Asp.net SignalR 实现服务端消息推送到Web端
之前的文章介绍过Asp.net SignalR, ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信. 今天我 ...
随机推荐
- [Angular] @ContentChildren and QueryList
We have looked at @ContentChild in article(http://www.cnblogs.com/Answer1215/p/6414657.html). Now le ...
- 学习鸟哥的Linux私房菜笔记(3)——基础使用
一.设备文件 设备在Linux中以特殊文件的形式存在 块(block)设备文件 字符(character)设备文件 设备文件所在位置 查看设备类型 二.虚拟控制台及用户身份切换 在系统中有12个虚拟控 ...
- java中<T> T和T的区别?
如果你希望 getMax 方法的返回值类型为 T,就要这样去定义getMax方法: public T getMax() 如果你希望 getMax 方法返回值的类型由调用者决定,那么就这么去定义 get ...
- Android应用《撕开美女衣服》的实现过程及源代码
现在很多Android市场中都能找到关于美女的应用,比如 撕开美女衣服.吹裙子等. 这些应用的下载量挺大的,作为Android的开发人员或者一名技术人员我们不能只局限在欣赏应用的层面,很多时候需要我们 ...
- 经Gradle采取Jenkins的build
如今,企业都太多Jenkins去管理apk,后该代码被提交jenkins在生成build 因此,我们可以得到jenkins提交版本 Jenkins在编制job什么时候,有一个内置的可变BUILD_NU ...
- 编程算法 - 背包问题(三种动态规划) 代码(C)
背包问题(三种动态规划) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目參考: http://blog.csdn.net/caroline_wen ...
- go-wingui 2018 全新 v2.0 版本发布,包含重大更新!
go-wingui 2018 全新 v2.0 版本发布,包含重大更新!使用新版CEF内核Chromium 63.0.3239.109,页面可以使用最新的css3,html5技术.使用delphi7重写 ...
- 回归(regression)的理解(regressor,回归子)
1. 基本概念 回归(regression)是监督学习(given {(xi,yi)})的一个重要分类.回归用于预测输入变量(自变量,Xi)与输出变量(因变量,Yi) 之间的关系,特定是当输入变量的值 ...
- 第一次react-native项目实践要点总结 good
今天完成了我的第一个react-native项目的封包,当然其间各种环境各种坑,同时,成就感也是满满的.这里总结一下使用react-native的一些入门级重要点(不涉及环境).注意:阅读需要语法基础 ...
- “warning C4996: 'fopen': This function or variable may be unsafe”和“LINK : fatal error LNK1104”的解决办法
程序有时编译出现警告C4996,报错: warning C4996: 'fopen': This function or variable may be unsafe. Consider using ...