Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
SignalR, Microsoft tarafından geliştirilen ve real-time (gerçek zamanlı) uygulama geliştirmeyi kolaylaştıran bir kütüphanedir. Bu yazıda, SignalR kullanarak sunucu ve istemci uygulamalarının nasıl geliştirileceğini adım adım ele alacağız.
SignalR, .NET geliştiricileri için geliştirilmiş, real-time web uygulamaları oluşturmayı kolaylaştıran bir kütüphanedir. SignalR, sunucu ile istemci arasında çift yönlü iletişim sağlayarak, mesajların anında iletilmesini mümkün kılar. Bu, özellikle chat uygulamaları, canlı veri güncellemeleri, oyunlar ve diğer real-time gereksinimlerinde oldukça faydalıdır.
Klasik web uygulamalarında, sunucu ile istemci arasındaki iletişim genellikle HTTP istekleri üzerinden gerçekleştirilir. İstemci, sunucuya istek gönderir ve yanıt alır. Bu model, real-time uygulamalar için yeterli değildir çünkü sunucudan sürekli veri çekmek gereksinimi doğurur ve bu da performans ve kullanıcı deneyimi açısından sorunlara yol açabilir.
SignalR, bu sorunu çözmek için WebSocket protokolü üzerine inşa edilmiştir. WebSocket, sunucu ve istemci arasında sürekli açık bir bağlantı kurarak, iki tarafın da birbirine anında veri göndermesine olanak tanır. SignalR, bu bağlantıyı yönetir ve geliştiricilere kolay bir API sunar.
SignalR’ın temelinde, Hub adı verilen bir yapı bulunur. Hub, sunucu ile istemci arasında iletişimi yönetir. İstemciler Hub’a bağlanır ve sunucu, Hub üzerinden gelen mesajları diğer istemcilere iletir. Hub sınıfları, sunucu tarafında oluşturulur ve istemciler, bu Hub’lara bağlanarak sunucu ile iletişim kurar.
SignalR kullanarak bir sunucu uygulaması geliştirmek için aşağıdaki adımları takip etmeliyiz:
1. Adım: Proje Oluşturma
Öncelikle, ASP.NET Core uygulaması oluşturuyoruz. Visual Studio veya .NET CLI kullanarak boş bir ASP.NET Core uygulaması oluşturabilirsiniz.
dotnet new web -n SignalRExample
2. Adım: SignalR Kütüphanesini Yükleme
Proje oluşturulduktan sonra, SignalR kütüphanesini projeye dahil etmemiz gerekiyor. Bu, NuGet paket yöneticisi kullanılarak yapılabilir:
dotnet add package Microsoft.AspNetCore.SignalR
3. Adım: Hub Sınıfının Oluşturulması
SignalR uygulamalarında, sunucu tarafında bir Hub sınıfı tanımlanır. Bu sınıf, istemcilerden gelen mesajları alır ve diğer istemcilere iletir.
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Yukarıdaki kodda, ChatHub
sınıfı oluşturduk. Bu sınıf, istemcilerden gelen mesajları alır ve diğer tüm istemcilere bu mesajları iletir. SendMessage
metodu, istemciden bir kullanıcı adı ve mesaj alır ve bu mesajı diğer istemcilere iletir.
4. Adım: SignalR’ı Başlatma
SignalR’ı kullanabilmek için, Startup.cs
dosyasında gerekli ayarları yapmamız gerekiyor.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR(); // SignalR hizmetini ekliyoruz.
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chathub"); // SignalR Hub'ını tanımlıyoruz.
});
}
Bu yapılandırmada, SignalR’ı servislere ekledik ve uygulama pipeline’ına SignalR Hub’ını ekledik. MapHub<ChatHub>("/chathub")
satırı ile ChatHub
sınıfını /chathub
endpoint’i ile ilişkilendirdik.
SignalR istemci tarafında, JavaScript kullanarak sunucuya bağlanabilir ve mesaj gönderebiliriz.
1. Adım: SignalR Kütüphanelerini Dahil Etme
İstemci tarafında, SignalR JavaScript kütüphanesini kullanmamız gerekiyor. Bu kütüphaneyi projeye eklemek için HTML dosyanıza aşağıdaki script etiketlerini ekleyin:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.9/signalr.min.js"></script>
2. Adım: SignalR Bağlantısı Oluşturma
SignalR ile sunucuya bağlanmak için bir bağlantı oluşturmalıyız. Bu bağlantı, Hub ile iletişim kurmamızı sağlar.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.build();
connection.start().catch(err => console.error(err.toString()));
3. Adım: Mesaj Gönderme ve Alma
Artık istemci, sunucuya mesaj gönderebilir ve sunucudan gelen mesajları alabilir.
document.getElementById("sendButton").addEventListener("click", function (event) {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString()));
event.preventDefault();
});
connection.on("ReceiveMessage", function (user, message) {
const msg = user + " says " + message;
const li = document.createElement("li");
li.textContent = msg;
document.getElementById("messagesList").appendChild(li);
});
Yukarıdaki kodda, sendButton
butonuna tıklanıldığında, istemci sunucuya bir mesaj gönderir (SendMessage
metodunu çağırır). Sunucudan bir mesaj geldiğinde (ReceiveMessage
), bu mesaj istemci üzerinde bir listeye eklenir.
SignalR, gerçek zamanlı (real-time) uygulamalar geliştirmek için kullanılan güçlü bir kütüphanedir. Bu yazıda, SignalR’ın otomatik bağlantı konfigürasyonları ve durum fonksiyonları ile ilgili detayları inceleyeceğiz. Otomatik bağlantı denemeleri, bağlantı durumu takibi ve bu süreçte kullanıcıya bilgi verme gibi önemli konuları ele alacağız.
SignalR, istemci ve sunucu arasında sürekli bir bağlantı kurarak gerçek zamanlı iletişimi sağlar. Ancak bazı durumlarda bu bağlantı kopabilir veya baştan kurulamayabilir. Bu gibi durumlarda bağlantının otomatik olarak tekrar kurulması gerekebilir.
Bağlantının kopması durumunda, SignalR’ın otomatik olarak bağlantıyı tekrar denemesi için withAutomaticReconnect
fonksiyonu kullanılır. Bu fonksiyon, belirli aralıklarla bağlantıyı tekrar denemek için yapılandırılabilir.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([0, 2000, 10000, 30000])
.build();
Yukarıdaki örnekte, withAutomaticReconnect
fonksiyonu dört farklı deneme süresi ile yapılandırılmıştır:
0 ms
).Eğer 30 saniye içinde bağlantı tekrar sağlanamazsa, SignalR bağlantıyı tamamen keser ve yeniden denemeyecektir.
Bağlantı denemeleri için özel süreler tanımlamak mümkündür. Örneğin, bağlantı denemelerini her bir saniyede bir tekrar etmek isteyebilirsiniz:
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.withAutomaticReconnect([1000, 1000, 3000, 5000])
.build();
Bu yapılandırma, ilk denemenin bir saniye sonra, ikinci denemenin yine bir saniye sonra, üçüncü denemenin üç saniye sonra ve dördüncü denemenin beş saniye sonra yapılmasını sağlar.
SignalR, bağlantı durumlarına göre çeşitli olaylar (events) tetikler. Bu olaylar, bağlantı durumu değişikliklerini izlemek ve kullanıcıya bu durumu bildirmek için kullanılabilir.
onreconnecting
OlayıBağlantının koptuğu ve yeniden bağlanma denemelerinin başladığı durumlarda onreconnecting
olayı tetiklenir. Bu olay, kullanıcıya bağlantının yeniden kurulmaya çalışıldığını bildirmek için kullanılabilir.
connection.onreconnecting((error) => {
console.log("Bağlantı koptu, yeniden bağlanmaya çalışılıyor...");
document.getElementById("status").textContent = "Bağlantı koptu, yeniden bağlanmaya çalışılıyor...";
});
onreconnected
OlayıBağlantı yeniden sağlandığında onreconnected
olayı tetiklenir. Bu olay, bağlantının başarıyla yeniden kurulduğunu bildirmek için kullanılabilir.
connection.onreconnected((connectionId) => {
console.log("Bağlantı yeniden kuruldu.");
document.getElementById("status").textContent = "Bağlantı yeniden kuruldu.";
});
onclose
OlayıBağlantı denemeleri başarısız olursa ve SignalR bağlantıyı tamamen kapatırsa onclose
olayı tetiklenir. Bu olay, kullanıcıya bağlantının kesildiğini bildirmek için kullanılabilir.
connection.onclose((error) => {
console.log("Bağlantı tamamen kesildi.");
document.getElementById("status").textContent = "Bağlantı tamamen kesildi.";
});
Bağlantı durumlarını takip etmek ve kullanıcıya bilgi vermek için HTML’de basit bir durum göstergesi (status indicator) oluşturabiliriz.
<div id="status" style="display:none; color: white; background-color: red; padding: 10px;"></div>
Bağlantı durumu değiştiğinde bu durumu kullanıcıya gösterecek şekilde status
div’ini güncelleyebiliriz. Örneğin:
// onreconnecting olayında
document.getElementById("status").style.display = "block";
document.getElementById("status").style.backgroundColor = "orange";
document.getElementById("status").textContent = "Bağlantı koptu, yeniden bağlanmaya çalışılıyor...";
// onreconnected olayında
document.getElementById("status").style.backgroundColor = "green";
document.getElementById("status").textContent = "Bağlantı yeniden kuruldu.";
// onclose olayında
document.getElementById("status").style.backgroundColor = "red";
document.getElementById("status").textContent = "Bağlantı tamamen kesildi.";
Bu şekilde, kullanıcıya bağlantı durumu hakkında anında bilgi verebilirsiniz.
Bağlantı durumlarına göre farklı işlemler gerçekleştirmek için SignalR olaylarını kullanabiliriz. Örneğin, bağlantı koptuğunda kullanıcıya belirli bir mesaj göstermek veya yeniden bağlanma denemeleri sırasında belirli bir animasyon oynatmak gibi.
connection.onreconnecting((error) => {
// Yeniden bağlanma denemesi başlamadan önce yapılacak işlemler
console.log("Bağlantı kopmuş olabilir, yeniden bağlanıyor...");
// Kullanıcıya bilgi verme veya UI güncellemeleri yapma
});
connection.onreconnected((connectionId) => {
// Bağlantı yeniden sağlandığında yapılacak işlemler
console.log("Bağlantı tekrar kuruldu, ID: " + connectionId);
// UI'yı güncelleme
});
connection.onclose((error) => {
// Bağlantı tamamen kesildiğinde yapılacak işlemler
console.log("Bağlantı tamamen kapatıldı.");
// Kullanıcıya bilgi verme veya UI güncellemeleri yapma
});
SignalR, gerçek zamanlı uygulamalar geliştirmek için kullanılan güçlü bir kütüphanedir. Bu yazıda, SignalR kullanarak bağlantı olaylarını yönetme ve sistemdeki tüm client’ları listeleme konularını derinlemesine inceleyeceğiz. Bu süreçte, bağlantıların izlenmesi, client’ların haberdar edilmesi ve sistemdeki tüm client’ların listelenmesi gibi önemli adımları ele alacağız.
SignalR’da, bir client (istemci) sunucuya bağlandığında veya bağlantısını kestiğinde belirli olaylar (events) tetiklenir. Bu olaylar, bağlantıların izlenmesi ve yönetilmesi açısından önemlidir.
OnConnectedAsync
OlayıOnConnectedAsync
olayı, bir client sunucuya başarılı bir şekilde bağlandığında tetiklenir. Bu olay sayesinde, yeni bir client sisteme bağlandığında gerekli işlemleri yapabilir ve diğer client’ları bu bağlantıdan haberdar edebilirsiniz.
public class ChatHub : Hub
{
public override async Task OnConnectedAsync()
{
// Yeni bir client bağlandığında çalışacak kodlar
await Clients.All.SendAsync("UserConnected", Context.ConnectionId);
await base.OnConnectedAsync();
}
}
Bu örnekte, yeni bir client bağlandığında tüm diğer client’lara UserConnected
adında bir olay tetiklenir ve bağlanan client’ın ConnectionId
değeri gönderilir.
OnDisconnectedAsync
OlayıOnDisconnectedAsync
olayı, bir client sunucu ile bağlantısını kestiğinde veya bağlantısı kesildiğinde tetiklenir. Bu olay, bir client’ın sistemden çıktığında yapılması gereken işlemleri gerçekleştirmek için kullanılır.
public class ChatHub : Hub
{
public override async Task OnDisconnectedAsync(Exception exception)
{
// Bir client bağlantısını kestiğinde çalışacak kodlar
await Clients.All.SendAsync("UserDisconnected", Context.ConnectionId);
await base.OnDisconnectedAsync(exception);
}
}
Yukarıdaki örnekte, bir client bağlantısını kestiğinde tüm diğer client’lara UserDisconnected
adında bir olay tetiklenir ve bağlantıyı kesen client’ın ConnectionId
değeri gönderilir.
Sistemde hangi client’ların bağlı olduğunu ve bağlantıların ne zaman kesildiğini izlemek için, bu olayları kullanarak bir koleksiyon (list) oluşturabilirsiniz. Bu koleksiyon, bağlanan ve ayrılan client’ları izlemek için kullanılır.
Bağlanan tüm client’ları izlemek ve bir listeye eklemek için OnConnectedAsync
olayını kullanabilirsiniz:
public class ChatHub : Hub
{
private static List<string> connectedClients = new List<string>();
public override async Task OnConnectedAsync()
{
connectedClients.Add(Context.ConnectionId);
await Clients.All.SendAsync("UpdateClientList", connectedClients);
await base.OnConnectedAsync();
}
}
Bu kodda, bağlanan her client’ın ConnectionId
değeri connectedClients
listesine eklenir ve ardından bu liste tüm client’lara gönderilir.
Client bağlantısını kestiğinde, connectedClients
listesinden çıkarılması ve diğer client’lara güncellenmiş listenin gönderilmesi gerekir:
public class ChatHub : Hub
{
public override async Task OnDisconnectedAsync(Exception exception)
{
connectedClients.Remove(Context.ConnectionId);
await Clients.All.SendAsync("UpdateClientList", connectedClients);
await base.OnDisconnectedAsync(exception);
}
}
Bu kodda, ayrılan client’ın ConnectionId
değeri connectedClients
listesinden çıkarılır ve güncellenmiş liste tüm client’lara gönderilir.
SignalR kullanarak client’lar arasında gerçek zamanlı iletişim sağlamak için, server’da tetiklenen olayları client tarafında dinlemek gerekir.
Client tarafında, server’dan gönderilen UserConnected
, UserDisconnected
ve UpdateClientList
gibi olayları dinleyebilir ve buna göre kullanıcı arayüzünü (UI) güncelleyebilirsiniz.
const connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();
connection.on("UserConnected", function (connectionId) {
console.log("User connected: " + connectionId);
});
connection.on("UserDisconnected", function (connectionId) {
console.log("User disconnected: " + connectionId);
});
connection.on("UpdateClientList", function (clientList) {
// Tüm client'ları listeleme
const clientListElement = document.getElementById("clientList");
clientListElement.innerHTML = "";
clientList.forEach(clientId => {
const listItem = document.createElement("li");
listItem.textContent = clientId;
clientListElement.appendChild(listItem);
});
});
Bu JavaScript kodunda, server tarafından gönderilen olaylar dinlenir ve client’lar arası iletişim sağlanır.
SignalR kullanarak sistemdeki tüm client’ları listelemek ve bu listeyi güncel tutmak için yukarıdaki kodları kullanabilirsiniz. Bağlanan ve ayrılan her client, bu listeye eklenir veya çıkarılır ve güncel liste tüm client’lara gönderilir.
Client listesi, basit bir HTML liste (ul) yapısı ile kullanıcıya gösterilebilir:
<ul id="clientList"></ul>
Bu liste, UpdateClientList
olayı tetiklendiğinde JavaScript tarafından güncellenir.
SignalR, ASP.NET Core uygulamaları için gerçek zamanlı web işlevselliği sunan güçlü bir kütüphanedir. Bu yazıda, SignalR’ın iki önemli özelliği olan IHubContext
arayüzü ve Strongly Typed Hubs (Türü Belirlenmiş Hubs) özelliklerini detaylı olarak ele alacağız. Bu özellikler, SignalR kullanarak daha esnek, güvenli ve hataya dayanıklı uygulamalar geliştirmenizi sağlar.
IHubContext
, SignalR’da bir Hub’ın (merkezi iletişim noktası) dışında, farklı katmanlarda veya iş mantığında client’larla (istemcilerle) iletişim kurabilmenizi sağlayan bir arayüzdür. Bu arayüz, Hub sınıflarının dışında da SignalR bağlantıları üzerinden mesaj göndermenize olanak tanır.
SignalR Hub sınıfları, doğrudan client’lar ile iletişim kurmak için tasarlanmıştır. Ancak, bazen iş mantığınızda veya farklı sınıflarda, Hub sınıfının dışında SignalR üzerinden client’lara mesaj göndermeniz gerekebilir. Bu durumda, IHubContext
arayüzü devreye girer.
IHubContext
, Hub’ın dışında SignalR işlevlerini kullanarak client’larla iletişim kurmanıza olanak tanır. Bu, özellikle bağımsız sınıflarda veya servislerde SignalR işlevlerini kullanmanız gerektiğinde oldukça yararlıdır.
Öncelikle, IHubContext
arayüzünü kullanarak bir SignalR Hub’ı dışında client’lara nasıl mesaj gönderebileceğinizi inceleyelim.
public class MyBusinessService
{
private readonly IHubContext<MyHub> _hubContext;
public MyBusinessService(IHubContext<MyHub> hubContext)
{
_hubContext = hubContext;
}
public async Task SendMessageToAllClients(string message)
{
await _hubContext.Clients.All.SendAsync("ReceiveMessage", message);
}
}
Bu örnekte, MyBusinessService
adında bir servis sınıfı oluşturduk ve bu sınıf içerisinde SignalR Hub’ına bağımlı bir IHubContext
referansı tanımladık. SendMessageToAllClients
metodu, IHubContext
arayüzünü kullanarak tüm bağlı client’lara bir mesaj göndermektedir.
ASP.NET Core uygulamalarında IHubContext
, Dependency Injection (Bağımlılık Enjeksiyonu) mekanizması ile sağlanır. Bu, IHubContext
arayüzünün ilgili sınıflarda otomatik olarak enjekte edilmesini sağlar.
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddTransient<MyBusinessService>();
}
Yukarıdaki kod, MyBusinessService
sınıfını bağımlılık olarak enjekte edilebilir hale getirir. Bu sınıfı bir Controller içinde veya başka bir serviste kullanabilirsiniz.
SignalR’da Strongly Typed Hubs özelliği, client’lar ile server arasında daha güvenli ve hatasız bir iletişim kurmanıza yardımcı olur. Bu özellik, SignalR Hub’larında kullanılan metotların adlarını metinsel olarak değil, türü belirlenmiş bir arayüz aracılığıyla tanımlamanızı sağlar.
Strongly Typed Hubs, client ile server arasındaki iletişimde metin tabanlı hataları azaltmak ve derleme zamanında hataların tespit edilmesini sağlamak amacıyla kullanılır. Normalde SignalR’da metot isimleri metin olarak belirtilir ve bu, yazım hatalarına ve diğer sorunlara yol açabilir. Strongly Typed Hubs sayesinde bu metotlar bir arayüzde tanımlanır ve Hub’larda bu arayüzler üzerinden çağrılır.
Öncelikle, client’lar için bir arayüz tanımlayarak başlayalım:
public interface IClientMethods
{
Task ReceiveMessage(string message);
Task UserJoined(string userName);
Task UserLeft(string userName);
}
Bu arayüz, client’ların server’dan alabileceği metotları tanımlar. Şimdi bu arayüzü SignalR Hub’ımıza uygulayalım:
public class MyHub : Hub<IClientMethods>
{
public async Task SendMessageToAll(string message)
{
await Clients.All.ReceiveMessage(message);
}
}
Yukarıdaki örnekte, MyHub
sınıfı Hub<IClientMethods>
olarak tanımlanmıştır. Bu, ReceiveMessage
, UserJoined
ve UserLeft
metotlarının artık metin olarak değil, türü belirlenmiş bir arayüz (interface) üzerinden çağrılabileceği anlamına gelir.
Strongly Typed Hubs, metot isimlerinin yazım hatalarını önler ve kodun daha okunabilir olmasını sağlar. Bu sayede, derleme zamanında hatalar tespit edilerek runtime (çalışma zamanı) hataları en aza indirilir.
SignalR, ASP.NET Core projelerinde gerçek zamanlı web uygulamaları geliştirmenizi sağlayan güçlü bir kütüphanedir. Bu yazıda, SignalR’ın sunduğu Clients türleri ve bu türlerin kullanıldığı metotları detaylı olarak ele alacağız. Bu özellikler, farklı senaryolar için esnek ve özelleştirilmiş iletişim kurmanızı sağlar.
SignalR, client’lara mesaj gönderirken kullanabileceğiniz farklı türler sunar. Bu türler, hangi client’lara mesaj gönderileceğini kontrol etmenize olanak tanır. SignalR’da başlıca üç tür client vardır:
Caller
, yalnızca mesajı gönderen client’a mesaj iletilmesini sağlar. Örneğin, bir client kendi gönderdiği mesajın sonucunu sadece kendisi görmek istediğinde bu tür kullanılır.
public async Task SendMessageToCaller(string message)
{
await Clients.Caller.SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToCaller
metodu ile sadece mesajı gönderen client’a mesaj iletilir. Diğer client’lar bu mesajı almaz.
All
, mesajı gönderen dahil tüm client’lara mesaj gönderir. Bu tür, tüm client’ların aynı mesajı almasını istediğiniz senaryolarda kullanılır.
public async Task SendMessageToAll(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToAll
metodu ile mesaj, bağlı olan tüm client’lara gönderilir.
Others
, mesajı gönderen client hariç diğer tüm client’lara mesaj gönderir. Örneğin, bir gruba mesaj göndermek istediğinizde, mesajı gönderen hariç diğer tüm grup üyelerine mesaj iletilir.
public async Task SendMessageToOthers(string message)
{
await Clients.Others.SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToOthers
metodu ile mesaj, gönderen client hariç diğer tüm client’lara gönderilir.
SignalR, daha karmaşık senaryolar için de metotlar sunar. Bu metotlar, belirli gruplara veya client’lara mesaj göndermenizi sağlar. Bu metotlar arasında Client
, Group
, GroupExcept
, Clients
, User
, Users
gibi fonksiyonlar bulunur.
Client
, belirli bir client’a mesaj göndermenizi sağlar. Bu client, ConnectionId
ile tanımlanır.
public async Task SendMessageToClient(string connectionId, string message)
{
await Clients.Client(connectionId).SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToClient
metodu ile belirli bir client’a (connectionId ile tanımlanan) mesaj gönderilir.
Group
, belirli bir gruba mesaj göndermenizi sağlar. Grup üyeliği, client’ların gruplara eklenmesi veya çıkarılması ile kontrol edilir.
public async Task SendMessageToGroup(string groupName, string message)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToGroup
metodu ile belirli bir gruba (groupName ile tanımlanan) mesaj gönderilir. Grup dışındaki client’lar bu mesajı almaz.
GroupExcept
, bir gruba mesaj gönderirken, belirli client’ların bu mesajı almasını engeller. Örneğin, bir gruba mesaj göndermek istiyorsunuz ama bazı client’lar bu mesajı almayacak.
public async Task SendMessageToGroupExcept(string groupName, string[] excludedConnectionIds, string message)
{
await Clients.GroupExcept(groupName, excludedConnectionIds).SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToGroupExcept
metodu ile belirli bir gruba (groupName) mesaj gönderilir, ancak excludedConnectionIds
ile belirtilen client’lar bu mesajı almaz.
Groups
, birden fazla gruba aynı anda mesaj göndermenizi sağlar.
public async Task SendMessageToGroups(string[] groupNames, string message)
{
await Clients.Groups(groupNames).SendAsync("ReceiveMessage", message);
}
Bu örnekte, SendMessageToGroups
metodu ile belirtilen gruplara aynı anda mesaj gönderilir.
SignalR’da client türlerini kullanırken, genellikle SendAsync
metodu kullanılır. Bu metot, client’lara belirli bir metodu tetiklemeleri için mesaj gönderir.
SendAsync
, belirli bir metodu client’larda tetiklemek için kullanılır.
public async Task SendMessageToAll(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
Yukarıdaki örnekte, ReceiveMessage
metodu, tüm client’larda tetiklenir ve message
parametresi iletilir.
SignalR, sunucudan client’a veri göndermenin yanı sıra client’tan sunucuya veri göndermeyi de destekler. Bu durumda InvokeAsync
metodu kullanılır. Ancak genellikle server-to-client iletişimi için SendAsync
daha yaygındır.
Aşağıda, farklı senaryolar için SignalR’da kullanılabilecek kod örnekleri verilmiştir.
Bu örnekte, bir chat uygulamasında tüm client’lara mesaj göndermek için Clients.All
kullanılır:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Bir gruba mesaj göndermek için Clients.Group
kullanılır:
public class ChatHub : Hub
{
public async Task SendMessageToGroup(string groupName, string user, string message)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
}
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public async Task RemoveFromGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
}
Belirli bir kullanıcıya mesaj göndermek için Clients.User
kullanılır:
public async Task SendMessageToUser(string userId, string message)
{
await Clients.User(userId).SendAsync("ReceiveMessage", message);
}
SignalR, ASP.NET Core içerisinde yer alan, web uygulamalarında gerçek zamanlı (realtime) etkileşimler gerçekleştirebilmek için kullanılan bir kütüphanedir. Bu makalede, SignalR ile bir chat uygulaması geliştirmenin adımlarını inceleyeceğiz.
Uygulamanın temel tasarımında, kullanıcılar belirli odalara (gruplar) katılarak mesajlaşabilecekler. Uygulama, kullanıcının giriş yapmasıyla başlayacak ve kullanıcı adı sisteme kaydedilerek bir bağlantı (connection) kurulacak. Bağlantı sağlandığında, kullanıcının girdiği isim ile connection ID eşleştirilecek ve bu isim, kullanıcıya özgü işlemlerde kullanılacak.
Uygulamanın Temel Adımları:
SignalR’da gerçek zamanlı iletişim için bir Hub sınıfı oluşturulur. Bu sınıf, istemci ve sunucu arasındaki iletişimi yönetir. Örneğin, bir chat uygulaması için bir ChatHub
sınıfı oluşturulabilir.
public class ChatHub : Hub
{
private static readonly List<UserConnection> _connections = new List<UserConnection>();
public async Task SendMessage(string message, string groupName)
{
var user = _connections.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (user != null && !string.IsNullOrEmpty(groupName))
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user.Username, message);
}
}
public async Task JoinGroup(string groupName)
{
var user = _connections.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (user != null)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("UserJoined", user.Username);
}
}
public override Task OnConnectedAsync()
{
_connections.Add(new UserConnection
{
ConnectionId = Context.ConnectionId,
Username = Context.GetHttpContext().Request.Query["username"]
});
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
var user = _connections.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (user != null)
{
_connections.Remove(user);
}
return base.OnDisconnectedAsync(exception);
}
}
public class UserConnection
{
public string ConnectionId { get; set; }
public string Username { get; set; }
}
Her kullanıcı sisteme giriş yaptığında bir connection ID ile eşleştirilir ve bu bilgi sunucuda bir listede saklanır. Aynı şekilde, kullanıcılar belirli odalara (gruplar) katıldığında, bu bilgi de sunucu tarafından takip edilir. Örneğin, kullanıcılar odalara katıldığında veya odalardan ayrıldığında diğer kullanıcılar bilgilendirilir.
public async Task JoinRoom(string roomName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync("ReceiveMessage", $"{Context.ConnectionId} has joined the group {roomName}.");
}
public async Task LeaveRoom(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync("ReceiveMessage", $"{Context.ConnectionId} has left the group {roomName}.");
}
Kullanıcılar, odalara katıldıklarında veya bireysel olarak diğer kullanıcılara mesaj gönderebilirler. Mesajlar, kullanıcının ismiyle birlikte ilgili odaya veya kullanıcıya gönderilir.
public async Task SendMessageToGroup(string groupName, string message)
{
var user = _connections.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (user != null)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user.Username, message);
}
}
public async Task SendMessageToUser(string targetUser, string message)
{
var targetConnection = _connections.FirstOrDefault(x => x.Username == targetUser);
if (targetConnection != null)
{
await Clients.Client(targetConnection.ConnectionId).SendAsync("ReceiveMessage", Context.User.Identity.Name, message);
}
}
Kullanıcı arayüzünde (UI) kullanıcının mesaj gönderebilmesi ve odalar arasında geçiş yapabilmesi için gerekli butonlar ve event handler’lar tanımlanır.
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chathub")
.build();
connection.on("ReceiveMessage", (username, message) => {
const msg = username + " says " + message;
const li = document.createElement("li");
li.textContent = msg;
document.getElementById("messagesList").appendChild(li);
});
document.getElementById("sendButton").addEventListener("click", event => {
const message = document.getElementById("messageInput").value;
const groupName = document.getElementById("groupInput").value;
connection.invoke("SendMessageToGroup", groupName, message).catch(err => console.error(err.toString()));
event.preventDefault();
});
connection.start().catch(err => console.error(err.toString()));
SignalR ile Realtime (Gerçek Zamanlı) Uygulama Geliştirme başlığı altında, veritabanındaki değişiklikleri anlık olarak yakalayabilen bir uygulama geliştirmenin tüm adımlarını detaylandırarak anlatacağım. Bu süreçte, özellikle SignalR, Entity Framework, SQL Server Service Broker gibi teknolojiler kullanarak, veritabanındaki değişikliklerin anlık olarak nasıl takip edileceğini ve bu değişikliklerin kullanıcı arayüzüne gerçek zamanlı olarak nasıl yansıtılacağını adım adım inceleyeceğiz.
Öncelikle, bir Angular frontend ve bir ASP.NET Core backend uygulaması oluşturacağız. Frontend tarafında Angular kullanılmasının sebebi, SignalR ile entegrasyonun kolaylıkla yapılabilmesi ve kullanıcıya anlık geri bildirimler sunabilmemizdir. Backend tarafında ise ASP.NET Core ile SignalR’ı kullanarak veritabanı değişikliklerini anlık olarak takip edeceğiz.
Angular uygulamasını oluşturmak için aşağıdaki adımları izleyebilirsiniz:
ng new RealTimeApp
cd RealTimeApp
ng add @aspnet/signalr
Bu komutlarla Angular projemizi oluşturuyoruz ve SignalR için gerekli olan kütüphaneyi projeye dahil ediyoruz.
Aşağıdaki komutları kullanarak ASP.NET Core projemizi oluşturabiliriz:
dotnet new webapi -n RealTimeAPI
cd RealTimeAPI
dotnet add package Microsoft.AspNetCore.SignalR
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.SqlServer.ServiceBroker
Bu komutlar, SignalR ve Entity Framework Core gibi gerekli kütüphaneleri ASP.NET Core projemize dahil eder.
Örnek uygulamamızda iki tablo kullanacağız: Personeller
ve Satislar
. Bu tablolarda personellerin yaptığı satışlar tutulacak ve bu veriler anlık olarak takip edilecektir.
CREATE TABLE Personeller (
Id INT PRIMARY KEY IDENTITY,
Ad NVARCHAR(100),
Soyad NVARCHAR(100)
);
CREATE TABLE Satislar (
Id INT PRIMARY KEY IDENTITY,
PersonelId INT,
Fiyat DECIMAL(18,2),
FOREIGN KEY (PersonelId) REFERENCES Personeller(Id)
);
Bu iki tablo arasında bir PersonelId
ilişkisi bulunuyor ve bu ilişkiyi Entity Framework Core kullanarak yöneteceğiz.
Entity Framework Core’u ASP.NET Core projemizde kullanarak bu tablo yapılarını modele döküyoruz.
public class AppDbContext : DbContext
{
public DbSet<Personel> Personeller { get; set; }
public DbSet<Satis> Satislar { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Satis>()
.HasOne(s => s.Personel)
.WithMany(p => p.Satislar)
.HasForeignKey(s => s.PersonelId);
}
}
public class Personel
{
public int Id { get; set; }
public string Ad { get; set; }
public string Soyad { get; set; }
public ICollection<Satis> Satislar { get; set; }
}
public class Satis
{
public int Id { get; set; }
public int PersonelId { get; set; }
public decimal Fiyat { get; set; }
public Personel Personel { get; set; }
}
ASP.NET Core tarafında SignalR ile haberleşmeyi sağlayacak olan bir Hub
sınıfı oluşturuyoruz.
public class SatisHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Bu Hub
sınıfı, gelen istekleri tüm bağlı kullanıcılara iletecek bir yapıya sahiptir.
SignalR middleware’ini uygulamaya eklemek için Startup.cs
dosyasına aşağıdaki kodları ekliyoruz:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddSignalR();
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("http://localhost:4200")
.AllowCredentials();
}));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<SatisHub>("/satisHub");
});
}
Bu ayarlar, SignalR’ın Angular frontend ile haberleşmesini sağlamak amacıyla gerekli CORS (Cross-Origin Resource Sharing) politikalarını da içermektedir.
Service Broker, SQL Server’da veritabanı değişikliklerini tetiklemek için kullanılan bir servistir. Service Broker’ı etkinleştirmek için aşağıdaki komutu kullanabilirsiniz:
ALTER DATABASE YourDatabaseName SET ENABLE_BROKER;
Service Broker, veri tabanında herhangi bir değişiklik olduğunda bir tetikleyici (trigger) aracılığıyla SignalR Hub’ına bu değişiklikleri bildirecektir.
Veritabanındaki değişiklikleri yakalayabilmek için SqlDependency
sınıfını kullanacağız. Bu sınıf, SQL Server’dan veri değişikliklerini dinleyip, SignalR Hub’ına bildirim yapmamızı sağlar.
public class SatisListener
{
private readonly AppDbContext _context;
private readonly IHubContext<SatisHub> _hubContext;
public SatisListener(AppDbContext context, IHubContext<SatisHub> hubContext)
{
_context = context;
_hubContext = hubContext;
}
public void Start()
{
SqlDependency.Start(_context.Database.GetDbConnection().ConnectionString);
WatchSatislar();
}
private void WatchSatislar()
{
using var command = _context.Database.GetDbConnection().CreateCommand();
command.CommandText = "SELECT Id, Fiyat FROM dbo.Satislar";
var dependency = new SqlDependency(command);
dependency.OnChange += async (sender, e) =>
{
if (e.Type != SqlNotificationType.Change)
return;
var satislar = await _context.Satislar.Include(s => s.Personel).ToListAsync();
await _hubContext.Clients.All.SendAsync("ReceiveSatisUpdates", satislar);
WatchSatislar(); // Yeniden izlemeye başla
};
command.ExecuteReader();
}
public void Stop()
{
SqlDependency.Stop(_context.Database.GetDbConnection().ConnectionString);
}
}
Angular frontend tarafında SignalR’ı kullanarak, arka plandan gelen veri güncellemelerini almak için aşağıdaki gibi bir servis oluşturuyoruz:
import { Injectable } from '@angular/core';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SatisService {
private hubConnection: HubConnection;
private satislarSubject = new BehaviorSubject<any[]>([]);
satislar$ = this.satislarSubject.asObservable();
constructor() {
this.hubConnection = new HubConnectionBuilder()
.withUrl('http://localhost:5000/satisHub')
.build();
this.hubConnection.start()
.catch(err => console.error(err));
this.hubConnection.on('ReceiveSatisUpdates', (satislar) => {
this.satislarSubject.next(satislar);
});
}
}
Bu servis, SignalR üzerinden gelen veri güncellemelerini yakalar ve bu verileri Angular bileşenlerine aktarır.
Alınan verileri Angular’da grafiksel olarak göstermek için bir kütüphane (örneğin highcharts
) kullanabiliriz. Bu kütüphane verileri grafik olarak görselleştirir ve kullanıcıya anlık veri akışını sunar.
import { Component, OnInit } from '@angular/core';
import { SatisService } from './satis.service';
import * as Highcharts from 'highcharts';
@Component({
selector: 'app-satis-chart',
template: '<div id="chartContainer"></div>'
})
export class SatisChartComponent implements OnInit {
Highcharts: typeof Highcharts = Highcharts;
chartOptions: Highcharts.Options;
constructor(private satisService: SatisService) {}
ngOnInit() {
this.satisService.satislar$.subscribe(satislar => {
this.updateChart(satislar);
});
}
updateChart(satislar) {
this.chartOptions = {
chart: {
type: 'bar'
},
title: {
text: 'Personel Satışları'
},
xAxis: {
categories: satislar.map(s => s.Personel.Ad)
},
yAxis: {
title: {
text: 'Satış Miktarı'
}
},
series: [{
name: 'Satışlar',
data: satislar.map(s => s.Fiyat)
}]
};
Highcharts.chart('chartContainer', this.chartOptions);
}
}
Bu rehberde, SignalR kullanarak dağıtık bir sistemde gerçek zamanlı iletişim sağlayan bir uygulama geliştireceğiz. Bu süreçte RabbitMQ gibi mesaj kuyruk sistemlerini kullanarak, büyük ölçekli ve performanslı uygulamalar nasıl geliştirilebilir, bunu detaylandıracağız.
SignalR, ASP.NET Core ile entegre çalışan, tarayıcılar ve sunucu arasında gerçek zamanlı (realtime) iletişimi sağlayan bir kütüphanedir. SignalR, özellikle web tabanlı uygulamalarda kullanıcı deneyimini iyileştirmek için kullanılır. Bu teknoloji, tarayıcıların ve sunucuların birbirleriyle gerçek zamanlı olarak iletişim kurmasını sağlar.
RabbitMQ ise bir mesaj kuyruk sistemidir. Bu tür sistemler, mesajların bir kuyruğa eklenmesini ve bu mesajların sıralı olarak işlenmesini sağlar. RabbitMQ, yüksek performans ve ölçeklenebilirlik gerektiren uygulamalarda yaygın olarak kullanılır.
İlk olarak, Angular frontend ve ASP.NET Core backend olmak üzere iki katmandan oluşan bir proje oluşturacağız. Frontend tarafında Angular kullanılmasının sebebi, SignalR ile entegrasyonun kolaylıkla yapılabilmesi ve kullanıcıya anlık geri bildirimler sunabilmemizdir. Backend tarafında ise ASP.NET Core ile SignalR’ı kullanarak veritabanı değişikliklerini anlık olarak takip edeceğiz.
Angular uygulamasını oluşturmak için aşağıdaki adımları izleyebilirsiniz:
ng new RealTimeApp
cd RealTimeApp
ng add @aspnet/signalr
Bu komutlarla Angular projemizi oluşturuyoruz ve SignalR için gerekli olan kütüphaneyi projeye dahil ediyoruz.
Aşağıdaki komutları kullanarak ASP.NET Core projemizi oluşturabiliriz:
dotnet new webapi -n RealTimeAPI
cd RealTimeAPI
dotnet add package Microsoft.AspNetCore.SignalR
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.SqlServer.ServiceBroker
Bu komutlar, SignalR ve Entity Framework Core gibi gerekli kütüphaneleri ASP.NET Core projemize dahil eder.
Bu projede, bir RabbitMQ kuyruğu oluşturup, gelen istekleri bu kuyruk üzerinden yöneteceğiz. Böylece, gelen yoğun isteklerin aynı anda işlenmesi yerine, kuyruğa alınarak sıralı bir şekilde işlenmesi sağlanacaktır.
SignalR, sunucu tarafında bir Hub
sınıfı kullanılarak yönetilir. Hub, istemcilerden gelen mesajları alır ve bu mesajları diğer istemcilere gönderir.
public class MessageHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Yukarıdaki Hub sınıfı, gelen mesajları tüm bağlı istemcilere yayınlar.
RabbitMQ kullanarak bir mesaj kuyruğu oluşturmak için aşağıdaki adımları takip edebiliriz:
using RabbitMQ.Client;
using System.Text;
public class RabbitMQService
{
private readonly IConnection _connection;
private readonly IModel _channel;
public RabbitMQService()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
_connection = factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
}
public void SendMessage(string message)
{
var body = Encoding.UTF8.GetBytes(message);
var properties = _channel.CreateBasicProperties();
properties.Persistent = true;
_channel.BasicPublish(exchange: "",
routingKey: "task_queue",
basicProperties: properties,
body: body);
}
public void Close()
{
_channel.Close();
_connection.Close();
}
}
Yukarıdaki RabbitMQService
sınıfı, RabbitMQ’ya bağlanmak ve mesajları kuyruğa göndermek için kullanılır.
RabbitMQ’da mesajları tüketen bir konsol uygulaması geliştiriyoruz:
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
public class RabbitMQConsumer
{
public void StartConsumer()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
};
channel.BasicConsume(queue: "task_queue",
autoAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
Bu kod, RabbitMQ kuyruğundan mesajları alır ve konsolda görüntüler.
SignalR ve RabbitMQ entegrasyonu, istemciden gelen mesajların RabbitMQ kuyruğuna eklenmesini ve daha sonra bu kuyruğun tüketilerek, istemcilere geri bildirim verilmesini sağlar.
Örnek Senaryo: Kullanıcı bir mesaj gönderdiğinde, bu mesaj RabbitMQ kuyruğuna eklenir. Kuyrukta bu mesaj işlendiğinde, tüm bağlı istemcilere mesajın işlendiğine dair bilgi verilir.
RabbitMQ’ya eklenen bir mesajın işlendiğinde SignalR Hub üzerinden tüm istemcilere bildirim yapılması için aşağıdaki adımları takip edebiliriz:
public class MessageProcessor
{
private readonly IHubContext<MessageHub> _hubContext;
public MessageProcessor(IHubContext<MessageHub> hubContext)
{
_hubContext = hubContext;
}
public async Task ProcessMessage(string message)
{
// Mesaj işleme mantığı burada olacak (örneğin: bir veritabanına kaydetme)
await _hubContext.Clients.All.SendAsync("ReceiveMessage", "Server", message);
}
}
Angular tarafında SignalR kullanarak anlık güncellemeleri almak için şu adımları izleyebilirsiniz:
import { Injectable } from '@angular/core';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SignalRService {
private hubConnection: HubConnection;
private messageSource = new BehaviorSubject<string>("");
currentMessage = this.messageSource.asObservable();
constructor() {
this.hubConnection = new HubConnectionBuilder()
.withUrl('http://localhost:5000/messageHub')
.build();
this.hubConnection.start().catch(err => console.error(err.toString()));
this.hubConnection.on('ReceiveMessage', (user: string, message: string) => {
this.messageSource.next(message);
});
}
}
Bu servis, SignalR Hub’dan gelen mesajları dinler ve bileşenlere iletir.
Bir sonraki yazıda görüşmek dileğiyle!”