مقدمة: لماذا WebSocket هو المعيار لتطبيقات real-time في Java
تتطلب تطبيقات الويب الحديثة تبادلاً فورياً للبيانات بين العميل والخادم. بروتوكول HTTP التقليدي، القائم على نموذج "طلب-استجابة"، غير مناسب للمهام التي تتطلب زمن استجابة ضئيلاً: الدردشات المباشرة، أسعار الأسهم، الألعاب متعددة اللاعبين، أو لوحات المراقبة في الوقت الفعلي.
بروتوكول WebSocket يحل هذه المشكلة عن طريق إنشاء اتصال ثنائي الاتجاه دائم بين العميل والخادم. بعد المصافحة الأولية HTTP، يتم "ترقية" الاتصال إلى WebSocket، وتُنقل البيانات على شكل إطارات بدون رؤوس إضافية. توفر Java، بفضل مكتبتها القياسية القوية وأطر العمل، أدوات ممتازة للعمل مع WebSocket.
في هذه المقالة، سننظر في كيفية إنشاء تطبيق real-time كامل بلغة Java باستخدام Jakarta WebSocket API (المعروف سابقاً بـ Java EE WebSocket). ستتعرف على إعداد الخادم، معالجة الرسائل، إدارة الجلسات، والحلول المعمارية النموذجية.
أساسيات العمل مع WebSocket في Java: Jakarta WebSocket API
بدءاً من Java EE 7 (Jakarta EE)، تمت إضافة JSR 356 إلى المنصة القياسية - Java API لـ WebSocket. هذا يعني أنه لإنشاء خادم WebSocket، لا تحتاج إلى إضافة مكتبات خارجية - يكفي وجود حاوية servlet تدعم Jakarta EE (مثل Apache Tomcat 9+، Jetty 10+، WildFly).
إنشاء نقطة نهاية WebSocket
العنصر الأساسي هو فئة نقطة النهاية، المُعلّمة بـ @ServerEndpoint. بداخلها، نحدد الدوال التي سيتم استدعاؤها عند الاتصال، استلام رسالة، خطأ، أو إغلاق الاتصال.
import jakarta.websocket.*;import jakarta.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.Set;import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/chat")public class ChatEndpoint {
private static final Set<Session> sessions = new CopyOnWriteArraySet<>();
@OnOpen public void onOpen(Session session) { sessions.add(session); broadcast("مستخدم متصل. العدد الإجمالي: " + sessions.size()); }
@OnMessage public void onMessage(String message, Session session) { broadcast("[" + session.getId() + "]: " + message); }
@OnClose public void onClose(Session session) { sessions.remove(session); broadcast("مستخدم غير متصل. العدد المتبقي: " + sessions.size()); }
@OnError public void onError(Session session, Throwable throwable) { System.err.println("خطأ في الجلسة " + session.getId() + ": " + throwable.getMessage()); }
private void broadcast(String message) { for (Session s : sessions) { if (s.isOpen()) { try { s.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } } }}في هذا المثال:
@ServerEndpoint("/chat")- يحدد أن نقطة النهاية متاحة عبر المسار النسبي/chat.CopyOnWriteArraySet- مجموعة آمنة للخيوط لتخزين الجلسات النشطة.@OnOpen- يُستدعى عند إنشاء الاتصال.@OnMessage- يُستدعى عند استلام رسالة نصية.@OnClose- يُستدعى عند إغلاق الاتصال.@OnError- معالجة الأخطاء.
JavaScript من جانب العميل
لاختبار الخادم، يكفي ملف HTML بسيط مع JavaScript:
<!DOCTYPE html><html><head> <title>WebSocket Chat</title></head><body> <div id="messages"></div> <input type="text" i