WebSockets ja reaaliaikaiset järjestelmät Springillä · Oppitunti

Julkisten chat-huoneiden toteuttaminen

Kehittäkää toiminnallisuus, jonka avulla käyttäjät voivat liittyä julkisiin yleislähetyksen kaltaisiin chat-huoneisiin ja osallistua niissä keskusteluun.

Oppitunti 2/411 vaihetta

Julkisten chat-huoneiden toteuttaminen on ilmainen WebSockets ja reaaliaikaiset järjestelmät Springillä-oppitunti CoddyKitissä. Tämä on oppitunti 2/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu WebSockets ja reaaliaikaiset järjestelmät Springillä-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. WebSockets ja reaaliaikaiset järjestelmät Springillä-kurssilla on yhteensä 4 oppituntia.

Julkiset chat-huoneet selitettynä

Tervetuloa! Tässä oppitunnissa rakennamme julkisia chat-huoneita. Ne ovat yhteisiä tiloja, joissa useat käyttäjät voivat lähettää ja vastaanottaa viestejä, jotka näkyvät kaikille huoneessa oleville.

Ajattele viestisovelluksen yleistä keskustelukanavaa. Keskeinen ominaisuus on yleislähetysviestintä: yhden käyttäjän lähettämä viesti välitetään välittömästi kaikille muille käyttäjille, jotka ovat parhaillaan huoneessa.

STOMP-aiheet huoneita varten

Springissä ja STOMPissa käytämme aiheita chat-huoneiden esittämiseen. Aihe on kanavan kaltainen kohde, jonka asiakkaat voivat tilata. Aiheeseen lähetetyt viestit lähetetään kaikille sen tilaajille.

  • Julkisissa huoneissa käytetään usein yhteistä aihepolkua, kuten /topic/public-chat.
  • Asiakkaat tilaavat tämän aiheen vastaanottaakseen viestejä.
  • Asiakkaat lähettävät viestejä tiettyyn sovelluskohteeseen, josta palvelin välittää ne edelleen aiheeseen.

Yksinkertainen chat-viestimalli

Ennen chat-logiikan toteuttamista tarvitsemme viesteille yksinkertaisen rakenteen. Näin varmistetaan, että kaikki ymmärtävät lähetettävän datan.

Perustason ChatMessage voi sisältää viestin type-tyypin (esimerkiksi CHAT, JOIN ja LEAVE), sender-lähettäjän nimen sekä varsinaisen content-sisällön. Tässä on lyhyt katsaus:

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 + "'}";
    }
}

Palvelinpuolen chat-ohjain

Saapuvien chat-viestien käsittelyä ja lähettämistä varten luomme Spring-@Controller-komponentin. Tämä komponentti kuuntelee tiettyyn sovelluskohteeseen (esimerkiksi /app/chat.sendMessage) lähetettyjä viestejä ja käyttää sitten SimpMessageSendingOperations-rajapintaa (usein SimpMessagingTemplate-toteutusta) niiden lähettämiseen julkiseen aiheeseemme (/topic/public-chat).

Kokeilkaa tämän esimerkin suorittamista:

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");
    }
}

Käyttäjien liittymisen lähettäminen

Kun käyttäjä muodostaa ensimmäisen kerran yhteyden tai liittyy nimenomaisesti chat-huoneeseen, on hyvä käytäntö ilmoittaa siitä kaikille muille. Tämä voidaan tehdä lähettämällä erityinen JOIN-tyyppinen viesti.

@MessageMapping voi tunnistaa nämä liittymistapahtumat ja lähettää ne samaan julkiseen aiheeseen. Näin kaikki asiakkaat tietävät, kuka on liittynyt huoneeseen.

Laajennetaan ChatController-komponenttia käsittelemään JOIN-viestejä:

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");
    }
}

Asiakas: yhteyden muodostaminen ja tilaaminen

Asiakaspuolella (esimerkiksi JavaScriptiä ja SockJS/STOMP.js:ää käytettäessä) ensimmäinen vaihe on WebSocket-yhteyden muodostaminen ja sen jälkeen julkisen chat-aiheen tilaaminen.

Asiakas käyttää STOMP-asiakasta yhteyden muodostamiseen /ws-päätepisteeseen ja tilaa sitten aiheen /topic/public-chat vastaanottaakseen kaikki lähetetyt viestit.

// 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'
    }));
});

Asiakas: chat-viestien lähettäminen

Kun yhteys on muodostettu ja aihe tilattu, asiakkaat voivat lähettää chat-viestejä palvelimelle. Viestit lähetetään sovelluskohteeseen (esimerkiksi /app/chat.sendMessage), jota Spring-komponenttimme kuuntelee.

Palvelin käsittelee viestin ja lähettää sen sitten takaisin kaikille kohteen /topic/public-chat tilaajille, myös viestin lähettäjälle.

// 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!');

Chatin koko kulku

Hahmotellaan julkisen chat-huoneen koko kulku:

  1. Asiakas muodostaa yhteyden: Käyttäjän selain muodostaa yhteyden WebSocket-päätepisteeseen (/ws).
  2. Asiakas tilaa aiheen: Asiakas tilaa julkisen aiheen (/topic/public-chat) vastaanottaakseen viestejä.
  3. Asiakas liittyy: Asiakas lähettää JOIN-viestin palvelimelle (/app/chat.addUser).
  4. Palvelin lähettää liittymisilmoituksen: Palvelin vastaanottaa JOIN-viestin ja lähettää sen aiheeseen /topic/public-chat. Kaikki asiakkaat vastaanottavat ilmoituksen käyttäjän liittymisestä.
  5. Asiakas lähettää chat-viestin: Asiakas lähettää CHAT-viestin palvelimelle (/app/chat.sendMessage).
  6. Palvelin lähettää chat-viestin: Palvelin vastaanottaa CHAT-viestin ja lähettää sen aiheeseen /topic/public-chat. Kaikki asiakkaat vastaanottavat chat-viestin.

Julkisen chatin parhaat käytännöt

Kun rakennatte julkisia chat-huoneita, huomioikaa seuraavat seikat paremman käyttökokemuksen ja palvelimen suorituskyvyn varmistamiseksi:

  • Viestihistoria: Toteuttakaa tapa tallentaa aiemmat viestit ja hakea ne huoneeseen liittyville uusille käyttäjille.
  • Käyttäjien läsnäolo: Seuratkaa huoneessa aktiivisia käyttäjiä (esimerkiksi muistissa olevan hajautustaulun tai tietokannan avulla).
  • Taajuuden rajoittaminen: Estäkää roskapostitus rajoittamalla, kuinka usein käyttäjä voi lähettää viestejä.
  • Moderointi: Tarjotkaa moderaattoreille työkalut sopimattomien viestien poistamiseen ja käyttäjien estämiseen.
  • Skaalautuvuus: Harkitkaa suurissa julkisissa chateissa ulkoisia viestinvälittäjiä, kuten RabbitMQ:ta tai Kafkaa.

Julkisen chat-huoneen testi

Mitkä seuraavista väitteistä pitävät paikkansa toteutettaessa julkisia chat-huoneita Spring WebSocketsin ja STOMP:n avulla?

Kertaus: julkiset chat-huoneet

Tässä oppitunnissa opitte toteuttamaan julkisia, lähetyspohjaisia chat-huoneita Spring WebSocketsin ja STOMP:n avulla.

  • Näimme, kuinka STOMP-aiheet (esimerkiksi /topic/public-chat) toimivat huoneiden viestintäkanavina.
  • Opitte käyttämään palvelimella @MessageMapping- ja SimpMessageSendingOperations-komponentteja saapuvien viestien käsittelyyn ja niiden lähettämiseen.
  • Tutustuimme käyttäjien liittymistapahtumien käsittelyyn ja kaikkien osallistujien ilmoittamiseen.
  • Lopuksi käsittelimme lyhyesti asiakaspuolen toimintaa ja tärkeitä hyviä käytäntöjä vakaiden chat-sovellusten toteuttamiseksi.

Seuraavaksi perehdymme yksityisten kahdenkeskisten viestien toteuttamiseen!

Aloita maksutta

Opi WebSockets ja reaaliaikaiset järjestelmät Springillä tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
12
Oppitunnit
48

Usein kysytyt kysymykset

Onko oppitunti ”Julkisten chat-huoneiden toteuttaminen” ilmainen?

Kyllä – oppitunnin ”Julkisten chat-huoneiden toteuttaminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko WebSockets ja reaaliaikaiset järjestelmät Springillä-kurssin, päivitä CoddyKit PROhon. WebSockets ja reaaliaikaiset järjestelmät Springillä-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Julkisten chat-huoneiden toteuttaminen”?

Kehittäkää toiminnallisuus, jonka avulla käyttäjät voivat liittyä julkisiin yleislähetyksen kaltaisiin chat-huoneisiin ja osallistua niissä keskusteluun. Harjoittelet WebSockets ja reaaliaikaiset järjestelmät Springillä-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni WebSockets ja reaaliaikaiset järjestelmät Springillä-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin WebSockets ja reaaliaikaiset järjestelmät Springillä-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/4.

Kuinka kauan ”Julkisten chat-huoneiden toteuttaminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä WebSockets ja reaaliaikaiset järjestelmät Springillä-oppitunnilla?

Kyllä. Jokainen WebSockets ja reaaliaikaiset järjestelmät Springillä-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Chat-viestimallin suunnittelu
  2. Julkisten chat-huoneiden toteuttaminen
  3. Yksityisviestintä STOMP:lla
  4. Kirjoitusilmaisimet ja paikallaolo
← Takaisin: WebSockets ja reaaliaikaiset järjestelmät Springillä