مقدمة: لماذا يعتبر WebSocket على Kotlin اختيار المحترفين لأنظمة الوقت الفعلي
تتطلب تطبيقات الويب الحديثة استجابة فورية لإجراءات المستخدمين: الدردشات، الألعاب عبر الإنترنت، أسعار الأسهم، أنظمة المراقبة. بروتوكول HTTP، القائم على الطلب والاستجابة، غير فعال لمثل هذه المهام. هنا يظهر WebSocket على المسرح — وهو بروتوكول يوفر اتصالاً ثنائي الاتجاه مستمراً بين العميل والخادم.
توفر لغة Kotlin مع نظامها القوي للكوروتينات (coroutines) وإطار العمل Ktor بيئة مثالية لبناء تطبيقات الوقت الفعلي القابلة للتوسع والمتسامحة مع الأخطاء. في هذه المقالة، سنقوم بتحليل التقنيات المتقدمة للعمل مع WebSocket على Kotlin: بدءاً من بنية التطبيق وصولاً إلى الضبط الدقيق للأداء. ستتعلم كيفية تجاوز حدود خادم echo البسيط وبناء حل صناعي.
بنية تطبيق WebSocket على Kotlin: من البسيط إلى المعقد
الأساس: خادم Ktor وإضافة WebSockets
لنبدأ بالإعداد الأساسي. يعتبر إطار العمل Ktor المعيار الفعلي لبناء الخوادم غير المتزامنة على Kotlin. للعمل مع WebSocket، يجب إضافة الإضافة (plugin):
// build.gradle.ktsimplementation("io.ktor:ktor-server-websockets:$ktor_version")implementation("io.ktor:ktor-server-netty:$ktor_version")يبدو التهيئة الأساسية للخادم مع دعم WebSocket كالتالي:
import io.ktor.server.engine.*import io.ktor.server.netty.*import io.ktor.server.websocket.*import io.ktor.websocket.*import io.ktor.server.routing.*
fun main() { embeddedServer(Netty, port = 8080) { install(WebSockets) { pingPeriod = 15.seconds // Keep-alive ping timeout = 15.seconds // مهلة الاستجابة maxFrameSize = Long.MAX_VALUE // الحد الأقصى لحجم الإطار }
routing { webSocket("/chat") { // منطق التعامل مع الاتصال send("مرحباً بك في الدردشة!") for (frame in incoming) { if (frame is Frame.Text) { val text = frame.readText() // معالجة الرسالة } } } } }.start(wait = true)}لاحظ المعاملات pingPeriod و timeout. في بيئة الإنتاج، يجب ضبطهما بناءً على ظروف الشبكة لتجنب قطع الاتصال عند حدوث تأخيرات مؤقتة.
إدارة الجلسات: أكثر من مجرد قائمة من المقابس
على المستوى المتقدم، مجرد تخزين قائمة من DefaultWebSocketServerSession غير كافٍ. من الضروري إدارة الجلسات، وتجميعها حسب الغرف/القنوات، ومعالجة حالات قطع الاتصال بشكل صحيح.
class ChatRoom(private val name: String) { private val sessions = mutableSetOf<WebSocketSession>()
@Synchronized fun add(session: WebSocketSession) { sessions.add(session) broadcast("${session.call.request.local.remoteHost} انضم إلى $name") }
@Synchronized fun remove(session: WebSocketSession) { sessions.remove(session) broadcast("${session.call.request.local.remoteHost} غادر $name") }
@Synchronized fun broadcast(message: String) { sessions.forEach { session -> GlobalScope.launch { try { session.send(Frame.Text(message)) } catch (e: Exception) { // نسجل الخطأ ونزيل الاتصال المعطل remove(session) } } } }}نقطة مهمة: استخدام @Synchronized يحمي من حالة السباق (race condition) عند الوصول المتزامن إلى mutableSetOf. في الأنظمة عالية التحميل، يجب
```