Kod serwerowy można pisać na dwóch poziomach abstrakcji. Niskopoziomowo możemy wykorzystać tzw. Persistent Connection, natomiast prostszym modelem, wystarczającym do większości potrzeb są Huby. Hub można postrzegać jako zbiór metod wystawionych przez serwer do dwukierunkowej komunikacji z klientem. Nazwy metod powinny odpowiadać biznesowym operacjom, dlatego zasadna wydaje się analogia do kontrolerów ze wzorca MVC. Nie powinniśmy nigdzie w kodzie przechowywać referencji do naszych hubów - nie mamy kontroli nad ich cyklem życia, a zatem nie warto także przechowywać stanu bezpośrednio w hubie. Każda publiczna metoda może być wołana z zewnątrz (zostanie dodana do dynamicznie generowanego klienckiego proxy). Serwer może też wołać dowolną metodę kliencką dzięki Dynamic Language Runtime. Wywołanie takie sprowadzi się do serializacji metody i jej parametrów. Domyślnym serializatorem jest JSON.NET.
[HubName("modernChat")]
public class ModernChatHub : Hub
{
public void SendMessage(string message)
{
Clients.All.newMessageFrom(Context.ConnectionId);
Clients.Caller.acknowledged();
Clients.Others.postMessage(message);
}
public void Subscribe(string groupName)
{
Groups.Add(Context.ConnectionId, groupName);
}
public void Unsubscribe(string groupName)
{
Groups.Remove(Context.ConnectionId, groupName);
}
public void Hello(string groupName)
{
var msg = string.Format("Welcome from {0}",
groupName);
Clients.Group(groupName).greetings(msg);
}
public override Task OnConnected()
{
var dependencyResolver = GlobalHost.DependencyResolver;
var connectionManager = dependencyResolver.Resolve<IConnectionManager>();
var hubContext = connectionManager.GetHubContext<ModernChatHub>();
var all = hubContext.Clients.All;
all.connected(Context.ConnectionId);
return base.OnConnected();
}
}
Mamy do dyspozycji kilka możliwości przetwarzania wiadomości. Możemy skorzystać z property
Clients, wysyłając wiadomości do nadawcy, do wszystkich, do wszystkich poza nadawcą. Można także selektywnie wybierać odbiorców po id połączenia. Property
Context zawiera także cookies, tożsamość użytkownika czy query string.
Kolejnym modelem są grupy agregujące użytkowników wg pewnego klucza. Trzeba pamiętać, że SignalR w żaden sposób nie przechowuje stanu po stronie serwera, co otwiera znakomite możliwości skalowalności, ale też nie zapamięta nam tego, kto był w której grupie przy restarcie aplikacji.
Grupa jest zatem jedynie zrzeszeniem kilku połączeń. Grupy są tworzone w sposób dynamiczny (jeżeli łączymy się do grupy, która nie istnieje, to zostanie założona). Jedno połączenie może być skojarzone z wieloma grupami.
Ostatni przykład, to zdarzenia, do których mamy dostęp z poziomu Hub-a, takie jak
OnConnected, OnDisconnected oraz
OnReconnected. Wewnątrz nich możemy wołać dowolny kod. Jeżeli chcemy
notyfikować podłączonych klientów spoza Huba, możemy skorzystać z
DependencyResolvera (odpowiednik ServiceLocatora).