0Pricing
WebSockets & Real-Time Systems with Spring · درس

تنفيذ غرف الدردشة العامة

طوّروا وظائف تتيح للمستخدمين الانضمام إلى غرف دردشة عامة والمشاركة فيها بأسلوب البث.

تنفيذ غرف الدردشة العامة درس مجاني في WebSockets & Real-Time Systems with Spring على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في WebSockets & Real-Time Systems with Spring، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة WebSockets & Real-Time Systems with Spring 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

Public Chat Rooms Explained

Welcome! In this lesson, we'll build public chat rooms. These are common spaces where many users can send and receive messages, visible to everyone in that room.

Think of a general discussion channel in a messaging app. The key characteristic here is broadcast communication: one message sent by any user is instantly relayed to all other users currently in the room.

STOMP Topics for Rooms

In Spring with STOMP, we use topics to represent chat rooms. A topic is like a channel clients can subscribe to. Messages sent to a topic are broadcast to all its subscribers.

  • For public rooms, we often use a common topic path like /topic/public-chat.
  • Clients subscribe to this topic to receive messages.
  • Clients send messages to a specific application destination, which the server then forwards to the topic.

Simple Chat Message Model

Before implementing the chat logic, we need a simple structure for our messages. This helps ensure everyone understands the data being sent.

A basic ChatMessage might include the message type (e.g., CHAT, JOIN, LEAVE), the sender's name, and the actual content. Here's a quick look:

public class ChatMessage {
    public enum MessageType {
        CHAT,
        JOIN,
        LEAVE
    }
    private MessageType type;
    private String content;
    private String sender;

    public ChatMessage() {}
    public MessageType getType() { return type; }
    public void setType(MessageType type) { this.type = type; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public String getSender() { return sender; }
    public void setSender(String sender) { this.sender = sender; }

    @Override
    public String toString() {
        return "ChatMessage{" + "type=" + type + ", sender='" + sender + "', content='" + content + "'}";
    }
}

Server-Side Chat Controller

To handle incoming chat messages and broadcast them, we'll create a Spring @Controller. This controller will listen for messages sent to a specific application destination (e.g., /app/chat.sendMessage) and then use a SimpMessageSendingOperations (often SimpMessagingTemplate) to broadcast them to our public topic (/topic/public-chat).

Try running this example:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

// ChatMessage POJO (for simplicity, included here)
class ChatMessage {
    public enum MessageType { CHAT, JOIN, LEAVE }
    private MessageType type;
    private String content;
    private String sender;
    public ChatMessage() {}
    public MessageType getType() { return type; }
    public void setType(MessageType type) { this.type = type; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public String getSender() { return sender; }
    public void setSender(String sender) { this.sender = sender; }
    @Override
    public String toString() {
        return "ChatMessage{" + "type=" + type + ", sender='" + sender + "', content='" + content + "'}";
    }
}

// WebSocket Configuration
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }
}

// Chat Controller
@Controller
class ChatController {
    private final SimpMessageSendingOperations messagingTemplate;

    public ChatController(SimpMessageSendingOperations messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageMapping("/chat.sendMessage")
    public void sendMessage(@Payload ChatMessage chatMessage) {
        System.out.println("Received CHAT message from " + chatMessage.getSender() + ": " + chatMessage.getContent());
        messagingTemplate.convertAndSend("/topic/public-chat", chatMessage);
    }
}

// Main Application
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
        System.out.println("\n--- Spring Boot Chat Server Started ---");
        System.out.println("Connect to ws://localhost:8080/ws");
        System.out.println("Send CHAT messages to /app/chat.sendMessage and subscribe to /topic/public-chat");
    }
}

Broadcasting User Joins

When a user first connects or explicitly "joins" a chat room, it's good practice to notify everyone else. We can do this by sending a special JOIN type message.

Our @MessageMapping can detect these join events and broadcast them to the same public topic. This way, all clients know who has entered the room.

Let's extend our ChatController to handle JOIN messages:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

class ChatMessage {
    public enum MessageType { CHAT, JOIN, LEAVE }
    private MessageType type;
    private String content;
    private String sender;
    public ChatMessage() {}
    public MessageType getType() { return type; }
    public void setType(MessageType type) { this.type = type; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public String getSender() { return sender; }
    public void setSender(String sender) { this.sender = sender; }
    @Override
    public String toString() {
        return "ChatMessage{" + "type=" + type + ", sender='" + sender + "', content='" + content + "'}";
    }
}

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }
}

@Controller
class ChatController {
    private final SimpMessageSendingOperations messagingTemplate;

    public ChatController(SimpMessageSendingOperations messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @MessageMapping("/chat.sendMessage")
    public void sendMessage(@Payload ChatMessage chatMessage) {
        System.out.println("Received CHAT message from " + chatMessage.getSender() + ": " + chatMessage.getContent());
        messagingTemplate.convertAndSend("/topic/public-chat", chatMessage);
    }

    @MessageMapping("/chat.addUser")
    public void addUser(@Payload ChatMessage chatMessage) {
        if (chatMessage.getType() == ChatMessage.MessageType.JOIN) {
            System.out.println(chatMessage.getSender() + " JOINED the chat!");
            messagingTemplate.convertAndSend("/topic/public-chat", chatMessage);
        }
    }
}

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
        System.out.println("\n--- Spring Boot Chat Server Started with JOIN handling ---");
        System.out.println("Connect to ws://localhost:8080/ws");
        System.out.println("Send JOIN messages to /app/chat.addUser");
        System.out.println("Send CHAT messages to /app/chat.sendMessage");
        System.out.println("Subscribe to /topic/public-chat");
    }
}

Client: Connect & Subscribe

On the client-side (e.g., using JavaScript and SockJS/STOMP.js), the first step is to establish a WebSocket connection and then subscribe to the public chat topic.

The client uses the STOMP client to connect to the /ws endpoint and then subscribes to /topic/public-chat to receive all broadcast messages.

// Assuming SockJS and STOMP.js are loaded
var socket = new SockJS('/ws'); // Connect to our WebSocket endpoint
var stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {
    console.log('Connected: ' + frame);
    // Subscribe to the public chat topic
    stompClient.subscribe('/topic/public-chat', function(message) {
        var chatMessage = JSON.parse(message.body);
        console.log('Received message: ' + chatMessage.content + ' from ' + chatMessage.sender);
        // Update UI with the message
    });

    // Send a JOIN message (example)
    stompClient.send("/app/chat.addUser", {}, JSON.stringify({
        sender: 'User123',
        type: 'JOIN'
    }));
});

Client: Sending Chat Messages

Once connected and subscribed, clients can send chat messages to the server. These messages are sent to the application destination (e.g., /app/chat.sendMessage) that our Spring controller is listening to.

The server then processes this message and broadcasts it back to all subscribers of /topic/public-chat, including the sender.

// Function to send a chat message
function sendChatMessage(senderName, messageContent) {
    if (stompClient && stompClient.connected) {
        var chatMessage = {
            sender: senderName,
            content: messageContent,
            type: 'CHAT'
        };
        stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(chatMessage));
        console.log("Sent: " + chatMessage.content);
    } else {
        console.log("STOMP client not connected.");
    }
}

// Example usage:
// sendChatMessage('Alice', 'Hello everyone!');

The Full Chat Flow

Let's visualize the complete flow for a public chat room:

  1. Client Connects: A user's browser connects to the WebSocket endpoint (/ws).
  2. Client Subscribes: The client subscribes to the public topic (/topic/public-chat) to receive messages.
  3. Client Joins: The client sends a JOIN message to the server (/app/chat.addUser).
  4. Server Broadcasts Join: The server receives the JOIN message and broadcasts it to /topic/public-chat. All clients receive the "user joined" notification.
  5. Client Sends Chat: A client sends a CHAT message to the server (/app/chat.sendMessage).
  6. Server Broadcasts Chat: The server receives the CHAT message and broadcasts it to /topic/public-chat. All clients receive the chat message.

Public Chat Best Practices

When building public chat rooms, consider these points for a better user experience and server performance:

  • Message History: Implement a way to store and retrieve past messages for new users joining the room.
  • User Presence: Track active users in a room (e.g., using an in-memory map or a database).
  • Rate Limiting: Prevent spamming by limiting how frequently a user can send messages.
  • Moderation: Provide tools for moderators to remove inappropriate messages or ban users.
  • Scalability: For large-scale public chats, consider external message brokers like RabbitMQ or Kafka.

Public Chat Room Check

Which of the following are true about implementing public chat rooms using Spring WebSockets and STOMP?

Recap: Public Chat Rooms

In this lesson, you learned how to implement public, broadcast-style chat rooms using Spring WebSockets and STOMP.

  • We saw how STOMP topics (e.g., /topic/public-chat) serve as the channels for rooms.
  • You learned to use @MessageMapping and SimpMessageSendingOperations on the server to handle incoming messages and broadcast them.
  • We explored how to handle user join events and notify all participants.
  • Finally, we touched upon client-side interaction and important best practices for robust chat applications.

Next, we'll dive into implementing private, one-on-one messaging!

الأسئلة الشائعة

هل درس «تنفيذ غرف الدردشة العامة» مجاني؟

نعم — نص درس «تنفيذ غرف الدردشة العامة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة WebSockets & Real-Time Systems with Spring، انتقل إلى CoddyKit PRO. تتضمن دورة WebSockets & Real-Time Systems with Spring 4 دروس في المجموع.

ماذا ستتعلم في «تنفيذ غرف الدردشة العامة»؟

طوّروا وظائف تتيح للمستخدمين الانضمام إلى غرف دردشة عامة والمشاركة فيها بأسلوب البث. تتمرن على WebSockets & Real-Time Systems with Spring مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ WebSockets & Real-Time Systems with Spring؟

لا تُشترط خبرة سابقة. WebSockets & Real-Time Systems with Spring على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «تنفيذ غرف الدردشة العامة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس WebSockets & Real-Time Systems with Spring هذا؟

نعم. كل درس في WebSockets & Real-Time Systems with Spring يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تصميم نموذج رسائل الدردشة
  2. تنفيذ غرف الدردشة العامة
  3. المراسلة الخاصة باستخدام STOMP
  4. مؤشرات الكتابة والحضور عبر الإنترنت
← العودة إلى WebSockets & Real-Time Systems with Spring