zoukankan      html  css  js  c++  java
  • 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();
    }
    

      

  • 相关阅读:
    遍历Map的四种方法
    过滤器和拦截器的区别
    拦截器、过滤器做什么的?
    Linux中常用操作命令
    什么是反射?
    得到字节码的方式有哪三种?
    web考试内容1
    spring注入方式
    PHP基础 第三天
    堆和栈,值传递和地址传递(引用传递)
  • 原文地址:https://www.cnblogs.com/xiaoai123/p/6659954.html
Copyright © 2011-2022 走看看