SignalR---服務端

前段時間把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();
}
相關文章
相關標籤/搜索