digression

There are too many things recently, and I haven’t updated for a long time. Thank you all for your continued attention. If you have any questions, please send me a private message to discuss with you.

An overview of the

This article, part 2 of part 3 of the WebSocket story series, provides a simple example of STOMP implementing point-to-point messaging based on the code described in the previous article. The WebSocket story series is planned as a six-part, step-by-step introduction to WebSocket and how to quickly build and use the capabilities WebSocket provides in Springboot.

This series is planned to include the following articles:

Part 1: What WebSocket is and what it Does Part 2: How to Use STOMP to Quickly Build WebSocket broadcast message Patterns in Spring In Springboot, how to use WebSocket and STOMP to quickly build point-to-point messaging mode (2) in Springboot, how to implement custom WebSocket message proxy in web chat rooms

The main line of this article

The last article started with @sendto and @sendtouser and went deep into Spring’s key code for sending WebSocket messages. This article will implement an example of point-to-point messaging based on STOMP and provide specific explanations.

In the process of writing this article, I also looked at some online examples, most of them have more or less problems, few can run, so I also give Github sample link at the end of the article, students who need to help themselves.

This article is suitable for readers

Students who want to learn about STOMP, the details of Spring’s internal code, and how to build WebSocket services using Springboot.

Implement a point-to-point messaging pattern

A, introducingWebSecurityImplementing User Management

When it comes to point-to-point messaging, imagine the common chat tools such as wechat and QQ, which have user management modules, including databases and so on. In order to simplify, we use WebSecurity to achieve a simple user login management based on memory. We can save two user information on the server and let the two users send messages to each other.

1. Introduce dependencies

<! -- add security module -->
<dependency>
<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code

2. ImplementWebSecurityConfig

Here we build two memory-level user accounts so that we can simulate messaging each other later.

package com.xnpe.chat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/"."/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/chat")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    // Declare two memory storage users
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    		auth
                .inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("Xiao Ming").password(new BCryptPasswordEncoder().encode("123")).roles("USER")
                .and().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("Suby").password(new BCryptPasswordEncoder().encode("123")).roles("USER");
    }

    @Override
    public void configure(WebSecurity web){
        web.ignoring().antMatchers("/resources/static/**"); }}Copy the code

Second, the implementationWebSocketAnd page configuration

With the two memory level user accounts set up, let’s configure websockets and pages.

1. Configure resource routes

package com.xnpe.chat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.addViewController("/chat").setViewName("chat");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); }}Copy the code

2. The configurationWebSocket STOMP

Here we register an Endpoint named Chat and a message broker named Queue.

package com.xnpe.chat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/Chat").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/queue"); }}Copy the code

Three, implementation,WebSocketMessage processing of

The client sends the message to chat at the specified address, which is captured and processed by handleChat. We have done some hard logic here. If the message is sent by Xiao Ming, we will route it to Suby. And vice versa.

1. Implementation of Controller

It is important to note that the Mapping address we are listening to is chat, so when the client sends messages later, pay attention to this address of the server. After receiving the message, the server will route the message to the address /queue/notification. In other words, our client WebSocket subscribed to the address /queue/notification.

package com.xnpe.chat.controller;

import com.xnpe.chat.data.Info;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.security.Principal;

@Controller
public class WebSocketController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @MessageMapping("/chat")
    public void handleChat(Principal principal, Info info) {
        if (principal.getName().equals("Xiao Ming")) {
            messagingTemplate.convertAndSendToUser("Suby"."/queue/notification", principal.getName() + " send message to you: "
                            + info.getInfo());
        } else {
            messagingTemplate.convertAndSendToUser("Xiao Ming"."/queue/notification", principal.getName() + " send message to you: "+ info.getInfo()); }}}Copy the code

2. Message Bean

A structure used to host messages exchanged

package com.xnpe.chat.data;

public class Info {

    private String info;

    public String getInfo(a) {
        returninfo; }}Copy the code

Compile the Html page of the client

1. Implement the login pagelogin.html


      
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
    <title>Landing page</title>
</head>
<body>
<div th:if="${param.error}">Invalid account and password</div>
<div th:if="${param.logout}">You have been logout</div>
<form th:action="@{/login}" method="post">
    <div><label>Account:<input type="text" name="username"/> </label></div>
    <div><label>Password:<input type="password" name="password"/> </label></div>
    <div><input type="submit" value="Login"/></div>
</form>
</body>
</html>
Copy the code

2. Implement chat pageschat.html

Two important points to emphasize:

  • When we connect to WebSocket, we specifyChatThis Endpoint. When sending a message, we need to send the message to the address mapped by the server, that is/chat.
  • Because the server will send the information to/queue/notificationSo we subscribe to this address as well, because we want to implement one-to-one messaging (according to the content of the previous article, you can refer to the previous article), here we need to add when subscribinguserPrefix.

      

<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
    <title>Welcome to the chat room</title>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery.js}"></script>
</head>
<body>
<p>The chat room</p>

<form id="chatForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit"/>
</form>

<script th:inline="javascript">
    $('#chatForm').submit(function(e){
        e.preventDefault();
        var text = $('#chatForm').find('textarea[name="text"]').val();
        sendSpittle(text);
        $('#chatForm').clean();
    });
    // Link to an endpoint named "/Chat".
    var sock = new SockJS("/Chat");
    var stomp = Stomp.over(sock);
    stomp.connect('abc'.'abc'.function(frame) {
        stomp.subscribe("/user/queue/notification", handleNotification);
    });

    function handleNotification(message) {$('#output').append("<b>Received: " + message.body + "</b><br/>")}function sendSpittle(text) {
        stomp.send("/chat", {}, JSON.stringify({ 'info': text }));
    }
    $('#stop').click(function() {sock.close()});
</script>

<div id="output"></div>
</body>
</html>
Copy the code

Demonstrates point-to-point messaging

Above, all the key code of our program has been implemented. After startup, access localhost:8080/login to get to the login page.

Open two separate pages and enter the account number and password (the two account information hard-coded in the code). The Chat page is displayed.

Enter information in the input box, then click Submit, and the message will be sent to another user.

code

The code project used in this article has been uploaded to Github for those who want to experience it.

GitHub-STOMP implements point-to-point messaging

conclusion

This article Outlines the basic steps for implementing point-to-point messaging based on STOMP. It is easy to note the address where the client sends the message and the address to which it subscribes. With STOMP, we implemented point-to-point messaging based on the user’s address, that is, STOMP implemented a mapping of the user’s address to the session, which also helped us easily send messages to the peer user without worrying about the details of the underlying implementation. But if we want to encapsulate more complex business logic ourselves, manage the user’s WebSocket session, and be more flexible in sending messages to the user, that’s what we’ll be looking at in our next article, without STOMP, to see how we can achieve more flexible WebSocket point-to-point communication.

Welcome to continue to follow

Xiaoming produced, must be a boutique

Welcome to pay attention to xNPE technology forum, more original dry goods daily push.