Ktor-Android(Kotlin) WebSocket ile Chat Uygulaması 💬

Fatih Kurçenli
7 min readNov 11, 2021

--

Photo by Adem AY on Unsplash

Günümüz pandemi döneminde anlık olarak mesajlaşma ve konuşma ihtiyacımız arttı.Birçok şirket ve kullanıcılar bu durumdan etkilendi. İnsanlar alternatif uygulamaları edinmeye ve indirmeye, hatta şirketler kendi uygulamalarını geliştirip kullanmaya başladı. Peki bu verileri nasıl gönderiliyor ve anlık olarak alıyorlar ? İşte bu kısımda WebSocket ve anlık haberleşme yapıları karşımıza çıkıyor. Şuan bir sürü chat uygulaması bu yapıyı günümüzde kullanıyor. Bu uygulamalar anlık olarak konuşmamızı sağlıyor ve veri iletimi sağlayarak mesajlaşma ve konuşma yapısını internet servisleri üzerinden sağlanıyor. Ayrıca bu sırada uygulama içerisinde pek çok özellikler de barındırıyorlar. Peki ya bu uygulamaları kendimiz yapmak istersek ?

Leyla ileMecnun

Gelelim önce bir backendimizi ayağa kaldıralım. Ktor uygulaması nasıl kurulur hemen bir bakalım. Öncelikle ide olarak ben intelij idea community’sini kullandım. Burada marketplace’den ktor’u pluginini ekliyoruz.

Sonrasında yeni bir projects kısmından yeni bir proje oluşturuyoruz. Ve Routing bizim endpointleri yöneteceğimiz kısım olacaktır. Sessions kullanıcıların ıd’leri sayesinde aynı kullanıcı ile içerideki HTTP istekleri yönetmesini sağlıyor. Websocket bildiğimiz üzere asıl konumuz olan anlık olarak verileri işleyeceğimiz ve android alanında dinleyeceğimiz yapı olacaktır. Authentication JWT ise kullanıcıların JWT token sayesinde tekrar giriş yapmadan JWT token bakarak kullanıcıyı uygulamaya girişini sağlamak için yapıdır. Daha detaylı bilgisi için bakınız. Ayrıca eğer verilerimizi Json formatından göndermek istiyorsak alt kısımdaki Gson yapısını seçip ilerliyoruz.

Uygulamamızın grup ıd ve artifact id siz dilediğiniz gibi verebilirsiniz. Project adımızı da veriyor ve projemizi oluşturuyoruz. Gradle dosyamızın gerekli kütüphanelerini yüklemesini bekliyoruz.

Harika, projemiz oluşturduk ve artık çalıştıralım ve backendimizin local’de çalıştığını görelim. Çalıştırmak için main fonksiyonumuzu çalıştırıyoruz.

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

Default olarak 8080 portunda çalışmaktadır. Eğer 8080 portu kullanan bir başka servisiniz varsa default portunu değiştirebilirsiniz. Değiştirmek isterseniz Resources içerisindeki application.conf dosyasındaki port değerini değiştirmeniz yeterli olacaktır. Localhost’da çalıştığından internet sitesinden http://localhost:8080 portuna istek attığımızda göreceğimiz hello world yapısını backend kodumuzdan bakalım.

get("/") {
call.respondText("HELLO WORLD!", contentType = ContentType.Text.Plain)
}

Görüldüğü üzere “/” istek attığımızda text yapısı olarak Hello World dönmekte. Yani backend servisimiz ayakta gözüküyor :) Şimdi gelelim bizim asıl konumuz olan Websocket yapımıza. Aynı şekilde peki istek atalım http://localhost:8080/myws/echo ne alacağız ? Hata mı bu değil çünkü websocket yapısı get isteği atmış oluyoruz. Bu kısımda artık websocket yapımızı dinleyebilmemiz için Postman ihtiyacımız oluyor. Postman indirme ve kurmak için bakınız.

Postman kurduk ve giriş yaptıktan sonra File->New diyerek bir yeni Websocket request yapımızı açıyoruz.

Şimdi localhost’daki istek yapımızı buraya yazıyoruz. Burada ufak bir değişikliğimiz var o da http değil artık WebSocket yapısı kullandığımızdan başına ws yapımız geliyor eğer WebSocket localde kullanıyorsak ws eğer SSL servisli bir yapı kullanıyorsak wss olmaktadır. Detaylı bilgi stackoverflow’da bu soruya cevap verilmiştir bakınız.

Harika artık servisimize bağlandık ve dinliyoruz. Servisimize bir yazı gönderelim ve dönen cevap yazdığımızda gelen bildirimi görüyorsunuz. Serviste bu yapıyı alıp geriye bize bir Text mesajı döndürülmekte. Sarı ışıkla giden yapımız bizim gönderdiğimiz text mesajı mavi ile gelen ise servisten bize dönen yapıyı görmekteyiz. Burada server sadece bize yazdıklarımızı geri döndürüyor ancak normal bir konuşma tabi ki böyle olmaz :) Şimdi backend tarafımızda birkaç değişiklik yapmamız gerekiyor. Src dosyamıza bir Connection sınıfı açıyoruz ve constructorda WebSocketSession’dan extend alan DefaultWebSocketSession interface ekliyoruz, Companion object içerisinde bir AtomicInteger tutuyoruz ve defaul olarak 0 değeri setliyoruz. AtomicInteger eşzamanlı olarak thread’lerin arttırma sağlamak istediğinde senkronize olarak arttırılmasını sağlamaktadır. İşletim sistemi derslerinde gördüğümüz Race Condition problemine çözüm sağlamaktadır. Ayrıca AtomicInteger bakınız;

İki ana kullanım alanı vardır AtomicInteger:

- Atomik bir sayaç olarak (incrementAndGet(), vb.) eşzamanlı olarak birçok iş parçacığı tarafından kullanılabilen.

- Karşılaştır ve takas talimatını destekleyen bir ilkel olarak (compareAndSet()) engellemeyen algoritmaları uygulamak için.

Application dosyamızdaki Routing scope altında bir connections listesi tutmaktayız kaç kullanıcı olduğunu bilgisini tutmak için. Chat uygulamamıza eklenen her bir kullanıcı Connection(this) objesi üreterek bu objeyi connections listemize ekliyoruz. (Her bir obje kendisine benzersiz bir kullanıcı atamaktadır)

val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())

Try catch ile uygulamamızı crash alması durumunda hatayı yakalamak ve kullanıcılara neden hata olduğunu belirtebiliriz. Şu anlık localde print yapıyoruz. İlk kullanıcı girdiğinde chat uygulamamıza bu chat uygulamasında kaç kişi online olduğunu belirten mesajı gönderiyoruz. Sonrasında gelen textleri dinlemeye başlıyoruz. Eğer gelen mesaj Text olarak gelmediyse uygulama devam ediyor. Eğer text olarak geliyorsa bu mesajı alıyor ve hangi kullanıcı attığını thisConnection sayesinde adını tutuyorduk objemizde bunu yazıyoruz ve yazdığı mesajı ekliyor ve bağlanan tüm kullanıcılara gönderiyoruz. Eğer kullanıcı disconnect olduğunda bu durumu finally durumunda bağlanan kullanıcı tüm kullanıcılara kim çıktığı hakkında bildirim gönderiyoruz ve connection yapımızdan o kişiyi düşürüyoruz.

Youtube

Eee biz bunu konuşmak için ettik, telefonuma mesaj gelmezse ben bu yapıyı neden yaptım :) Şimdi gelelim bu yapıyı Android tarafında nasıl dinleyeceğiz ? Hemen bir proje açalım ve aşağıdaki WebSocket kütüphanemizi ekleyelim. Bu kütüphane sayesinde WebSocket yapımıza bağlanıp dinleme işlemini yapacağız.

// WebSocket
implementation "org.java-websocket:Java-WebSocket:1.5.1"

Kütüphanemizi ekledikten sonrasında viewBinding ekliyoruz. Viewbinding tasarımımız olan XML ile ilişki kurmamızı ve XML’deki verilerimize erişmemizi sağlamakta. Bunun için ufak bir yapı ekliyoruz gradle dosyamıza. Daha detaylı bilgi için bakınız.

buildFeatures{
viewBinding true
}

Şimdi ufak bir tasarım yapıyorum. Ama siz siz olun benim gibi güzel UI’lar hazırlamayın :) Şaka bir yana daha güzel bir UI ile kullanıcılarınızı tutmanız gerekmektedir. Ben demo olarak yapıldığından dolayı basit bir text ,editText ve send butonu koyuyorum.

Tasarımımız tamamladıktan sonra diğer bir işlemimiz Utils objesi oluşturalım ve IP adresimizi ve port yapımızı yapalım neden mi ? Android emülatör localhost yapısı ile dinleyememekteyiz. Ufak bir arama yaptığımda localhost olarak android bu değeri yazmamızı istiyor lakin denedim olmadı :) 10.0.2.2 Sonuç olarak internet adresimizin IPv4 adresini almamız gerekiyor. Bunun için windows için cmd yazarak komut işlemcisini açalım ve ipconfig diyerek IPv4 adresimize erişelim. IPv4 adresinizi nasıl öğrenebilirsiniz buraya bakınız.

Bu değerlerimizi const val olarak Constants değerimizde tutuyoruz. Portumuz backendte ayarladığımız port olacaktır. Bu değerleri gitignore ekleyebilirsniz Constants değerlerinizi.

MainActivity’mize gelmeden önce kullandığımız trafik http olduğundan dolayı AndroidManifest.xml dosyamıza usesCleartextTraffic true olarak atamamız gerekmektedir. Aksi takdirde Android bize uyarıda bulunmaktadır. Eğer gerçek bir socket yapısıyla SSL güvenlikli bir yapıyla çıkıyorsanız bu değer setlenmesini kaldırın.

android:usesCleartextTraffic="true"

Gelelim kodumuza aslında çok basit bir yapımız var. ViewBinding setlememizi yapıyoruz. ve onDestroy kısmında ise _binding=null setliyoruz memory leak oluşturmaması için. Android Lifecycle’dan bildiğimiz gibi onResume aktif olduğunda initWebSocket yapımızı çalıştırıyoruz. initWebSocket yapımız URI olarak WEB_SOCKET_URL yapımızı veriyor ve WebSocket yapımızı ayağa kaldırıyoruz. Sonradan initlize edeceğimizi belirttiğimiz webSocket yapımıza WebSocketClient nesnesinden bir obje üretiyoruz ve setleme işlemini gerçekleştiriyoruz. Burada 4 metod karşılıyor bizi

onOpen → WebSockete bağlanıldığı ilk an olacaktır burada ilk bağlanıldığında servise göndermek istediğiniz parametreleri yazabilirsiniz.

onMessage → Burası en çok ilgilendiren konumuz bu kısımda her mesaj geldiğinde bu yapımız çalışacaktır ve mesajımızı buradan string olarak alıyoruz.

onClose → WebSockete disconnect olduğumuzda servise bildirebileceğimiz fonksiyondur.

onError → Burada WebSocket bağlanma hatalarını yakaladığımız yerdir

Gene bildiğimiz Android Lifecycle’dan eğer kullanıcı telefonu aşağıya veya kapanma durumda bu metod çalışacak ve WebSocket bağlanma yapımızı kapatacağız. Bu kısımda eğer sürekli çalışmasını istiyorsak. Bu yapımızı Workmanager yapımıza ekleyip dinleme işlemini arka kısımda halledebiliriz. Çok yakında Workmanager’larla ilgili bir yazı yazmayı düşünüyorum takipte kalın esen kalın :) Gelelim artık sonucumuz ne oldu →

Leyla ile Mecnun

💻Projelerin github linkleri →

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response