前段时间把SignalR的官网教程大体看了一下,算是翻译了一遍,加上了本身的我的理解,html
一下上传三个文件,分别是服务端、web客户端、DOTNET客户端相关文档,供你们参考。web
其中有一处因为翻译时不专心,但又没能找到的错误,是说$.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(); }