Asp.net Core 3.1+SignalR ile Real Time Web

Feyyaz Acet
6 min readSep 22, 2020

--

Merhaba arkadaşlar, iş yoğunluğu, yaz vs. derken yaklaşık olarak 2 aydır bir konu üzerine düşünüp yazmaktan uzak kaldım.
Günümüzün modern uygulamalarından bir refresh(yenileme) butonuna ihtiyaç duymadan güncel bilgiler sunması bekleniyor. Artık dashboardlarımızda, location uygulamalarımızda veya oyunlarda real time fonksiyonilitesi olmazsa olmazlarımızdan. Bundan yola çıkarak, ben de gittikçe popüler olmakla birlikte, ihtiyaç haline gelen gerçek zamanlı iletişimi (Real Time Communication) ele alacağım.

Geçmişten günümüze bir yolculuk olacak yazımda, bu iletişim ortamını sağlarken hangi yöntemlerden yararladığımızın üzerinden geçerek , Microsoft’un portal.azure.com içerisinde, yeni nesil web framework’ü olan Blazor’ın server side rendering(Blazor Server) seçeneğinde de alt yapısını oluşturduğu ve bugünün asıl konusu olan signalR’ı detaylandırıcağım.

Peki Signalr kullanmadan önce hangi yöntemlerden faydalandık?

Polling

pluralsight.com adresinden alınmıştır.

Belli aralıklarla(timeInterval) Http request yapılması ve varsa yeni bir event-message vs. alınması sürecidir. Kullanılan ilk yöntemlerden olmakla birlikte sunucuyu yorması, sürekli reqest-response yapması ile de en performansız yöntemdir diyebiliriz ama hakkını yemeyelim, tabiri caizse çok ekmeğini yedik 😊

Long Polling

pluralsight.com adresinden alınmıştır.

Long Polling, Http Requestlerin server tarafında bekletilmesi, bir event olduğunda veya time-out olduğunda isteğin tamamlanması sürecidir. İsteğin tamamlanması ile, hiç beklemeden yeni bir http request yapılır ve aynı süreç devam eder. Client-Server arasında her zaman aktif bir connection bulunmaktadır. Polling’in iyileştirilmiş bir versiyonu diyebiliriz.

Server-Sent Events

pluralsight.com adresinden alınmıştır.

Server-Sent Events (SSE), client tarafında HTTP bağlantısı aracılığıyla sunucudan otomatik olarak güncellemeleri almasını sağlayan bir sunucu push teknolojisidir. Html 5 ile hayatımıza girmiş olup w3c tarafından standartize edilmiştir. Bu yazıyı yazmadan önce kullanmamak ile birlikte, Event server tarafından push edilir. Bu yöntemde client tarafından mesajı iletmenin bir yolu yoktur. Bu iletişim yönetimi için modern browserserlar ortalama 4–10 arası connection’a kadar izin vermektedir.

WebSocket

pluralsight.com adresinden alınmıştır.

WebSocket, Mesajın server-client veya client-server şeklinde two way gidebildiği tek bir tcp connection üzerinde full-duplex channel sağlayan 2011 yılında IETF tarafından RFC 6455 ile stardartlaştırılmış bir yöntemdir. WebSocket api ise w3c tarafından standardize edilmiştir. Kuşkusuz en verimli real time data exchange yöntimidir. Modern browserlar tarafından aynı anda 50 civarında connectiona izin vermektedir.

pluralsight.com adresinden alınmıştır.

Tcp connectionda olduğu gibi, Websocket yönteminde de bir handshake söz konusu. Bir websocket lifetime’ını açıklayacak olursak, öncelikle bir client-server arasında handshake yapılır, akabinde data alışverişi ve en son var olan connection’ın tamamlanması olarak sıralayabiliriz.

Handshake sürecini biraz daha detaylandıracak olursak, client random bir string key gönderir. Server bu stringe sabit bir değer ekler ve hash işleminden sonra base64 e çevirip response Header’ında Sec-WebSocket-Accept key i ile döner. Daha detaylı bilgi için buraya link bırakıyorum.

SignalR

SignalR, yukarıda saydığımız tüm yöntemleri içinde barından(polling hariç) .net ekosistemi için yazılmış bir open source real time framework’üdür.

SignalR içerisinde LongPolling, Server Send Event(SSE), Websocket birer transport olarak adlandırılmaktadır.

pluralsight.com adresinden alınmıştır.

SignalR, client ile server arasında oluşan connectionda her iki taraf da browser bilgilerine sahip olmakla birlikte, client mevcut connection için en verimli transport seçeneği ile devam eder. Sırasıyla websocket, server send event ve Long polling şeklide devam eder. İstediğiniz takdirde bu duruma el koyup seçeneği kendiniz belirleyebilirsiniz.

Asp.net Core SignalR Avantajları Nelerdir

Cross Platform
Çoklu trasnsport(Websocket, Server Send Event(SSE), Long Polling) desteği
Hızlı
Light Weight

SignalR Önemli Kavramlar

Hub: Client ile server arasında data alışverişine aracılık eden server side class. Kısaca server-client arasındaki iletişim noktasıdır diyebiliriz.

Hub Protokol: Client-Server arasındaki data alışverişi için kabul edilen format’dır. Default Json kabul eder.

Artık yazımızı bir örnekle tamamlayabiliriz.

Örneğimizde anasayfada bulunan son siparişler kısmını, verilen siparişlerle birlikte dinamik olarak gücelleyeceğiz. Örnek projemizi visual studio üzerinde asp.net core 3.1 mvc proje template’i ile geliştireceğiz.

SignalRSample adında bir mvc projesi oluşturuyoruz. Hemen ardından signalR imlementasyonu için gerekli olan Microsoft.AspNetCore.SignalR.Core paketini projemize ekliyorum. Daha sonra client tarafı için gerekli paketi yüklemek için adımlar sırasıyla aşağıdaki gibidir.

Proje üzerinde sağ click yapıp Add > Client-Side Library.
Add Client-Side Library dialog’u açılacak.
Provider olarak unpkg secin.
Library alanına @microsoft/signalr@latest girin.
Choose Specifik Files seçeneğini tıklayın. dist/browser klasörünü genişletin ve signalr.js ve/veya signalr.min.js dosyalarını seçin.
Tatget Location olarak wwwroot/js/signalr girin ve Install edip tamamlayalım.

Öncelikle Client ile data alışverişimizi sağlayacak hub’ı mızı oluşturuyoruz. Bunun için Hubs adına bir klasör oluşturduktan sonra, Orderhub adında bir class ekliyorum. Ve SignalR core paketiyle gelen Hub abstract class’ından türetiyorum.

public class OrderHub:Hub{}

Aslında hub imlementasyonu için bu yeterli, ancak dilerseniz OnConnectedAsync ve OnDisconnectedAsync methodlarını ovveride edebilirsiniz.

public class OrderHub : Hub{public override Task OnConnectedAsync(){//get conntection Id Context.ConnectionId;return base.OnConnectedAsync();}public override Task OnDisconnectedAsync(Exception exception){return base.OnDisconnectedAsync(exception);}}

Uygulama içinde signalR’ı kullanmak için startup içerinde register işlemlerimizi gerçekleştiriyoruz. Container içerisine register işlemlerimizi gerçekleştirmek için ConfigureServices methodu içerisinde AddSignalR extension methodunu çağırıyoruz. Daha sonra hub ile ilgili endpointi tanımlamak için default olarak gelen endpoint tanımlamasını aşağıdaki şekilde değiştiriyorum.

app.UseEndpoints(endpoints =>{endpoints.MapHub<OrderHub>("/orderhub");endpoints.MapControllerRoute(name: "default",pattern: "{controller=Order}/{action=Index}/{id?}");});

Burada 2 şeye dikkat çekmek istiyorum. İlk olarak oluşturduğumuz hub(OrderHub) için route’ı “/orderhub” olarak bekledik. İkincisi, default route /Order/Index olarak belirledik.

Şimdi Order adında bir controller ekleyip, İlgili controller için sipariş verebileceğimiz ve son siparişleri göstereceğimiz bir şablon hazırlayarak index.cshtml sayfası oluşturuyorum. Muhteşem ötesi sayfamız aşağıdaki şekilde görünmektedir 😜

Şimdi siparişi karşılamak ve tüm clientlara iletmek için gerekli server-side kodlarını yazıyorum. OrderController son hali aşağdadaki gibidir.

Ne yaptığımıza göz atacak olursak; öncelikle constructor injection aracılığı ile hub’ı inject ettik. Burada IHubContext’i, hublarımızı yönetmek için abstraction bir yapı olarak düşünebiliriz. Akabinde sipariş almak için Order adında bir action ekledik. Ve gelen siparişleri lastOrder function aracılığı ile tüm clientlara iletiyoruz. “lastOrder” client tarafından tanımlanmış, OrderHub tarafından order push edilebilir şekilde bekleyen bir function’dır. Dilersek mesajı tüm clientlara değil, bir client’a veya bir kaç client’a yada belli cilent dışındakilere iletebiliriz.

Tüm clientlar için hub.Clients.All
Tek bir client için hub.Clients.Client(connectinId)
Bazı clientlar haric hub.Client.AllExcept

Şimdi sıra geldi client tarafındaki kodlarımıza. Öncelikle uygulama ayağa kalktığında Orderhub ile connection sağlayacak ve son siparişleri serverdan alacak lastOrder methodu için javascript kodlarımızı yazıyoruz.

<script>(function setupConnection() {var connection = new signalR.HubConnectionBuilder().withUrl("/orderHub").build();connection.on("lastOrder", function (order) {var newOrderNode = document.createElement("div");newOrderNode.classList.add("col-lg-12");var textnode = document.createTextNode(`${order.product} - ${order.size}`);newOrderNode.appendChild(textnode);var orderDiv = document.getElementById("lastOrders");orderDiv.insertBefore(newOrderNode, orderDiv.firstChild);});connection.start().catch(err => console.error(err.toString())).then(response => console.log("connected"));}) ();//setupConnection();function createOrder() {let size = parseInt( document.getElementById("size").value);var productDropdown = document.getElementById("product");let product = productDropdown.options[productDropdown.selectedIndex].value;fetch("/Order", {method: "POST",body: JSON.stringify({product, size}),headers: {'content-type': 'application/json'}}).then(response => console.log("order created"));}</script>

Sayfa açılırken signalR bağlantısını olulturmak için setupConnection methdunu IIFE (Immediately Invoked Function Expression) olarak tanımladık. HubConnectionBuilder vasıtasıyla OrderHub için belirlediğimiz endpoint’e bir connection oluşturuyoruz. lastOrder’ı da ilgili connection içersinde server’dan order’ı alıp son siparişler bölümüne append yapacak şekilde geliştirdik.

Not: Connection başarılı olduğu takdirde, Browser console tabında “connected” yazacaktır.

Ek olarak, createOrder methodunu oluşturduk. Bu methodun tek görevi Order’ı post etmektedir. /views/Order/ altındaki index.cshtml dosyamızın son hali aşağıdaki gibidir.

Evet artık test edebiliriz. Projemizi ayağa kaldırıyoruz. 3 farklı tarayıcıda order sayfamızı açıyoruz. Herşey yolunda gittiyse sipariş verdiğinizde sonuç aşağıdaki gibi olacaktır.

Sonuç

.Net ekosistemi içerisinde popüper olan Real Time Web frameworkü olan SignalR’dan bahsederken geçmişe giderek, nelerden geçtiğimize de kısaca göz attık. Umarım keyif aldığınız, bilgilendirici bir yazı olmuştur.

Projenin kodlarına buradan ulaşabilirsiniz.

Bir sonraki yazıda görüşmek dileğiyle, sağlıcakla kalın.

Kaynaklar

--

--