SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息

Posted kakarotto-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息相关的知识,希望对你有一定的参考价值。

SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息

参考:

1、https://www.mchweb.net/index.php/dev/887.html

2、https://itonline.blog.csdn.net/article/details/81221103?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-121078449.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-81221103-blog-121078449.pc_relevant_aa&utm_relevant_index=1

3、https://blog.csdn.net/yingxiake/article/details/51224569

使用场景

广播模式 :使用场景:给所有连接了这个通道的客户端发送消息。

  • convertAndSend()
  • @SendTo

点对点模式 :使用场景:单独给当前用户发送消息。

  • 下面两种方式,都默认加了一个前缀:/user

  • convertAndSendToUser()

  • @SendToUser

一、后端SpringBoot + WebSocket基础配置

1、导包

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2、配置config

  • 细节:必须配置跨域。低版本的SpringBoot(2.1.5.RELEASE 就不行)不行,需要使用高版本。低版本的解决方案还未找到。
  • 跨域配置使用:.setAllowedOriginPatterns("*")。。不能使用:.setAllowedOrigins("*")
package com.cc.ws.config;

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

/**
 * <p>@EnableWebSocketMessageBroker 的作用</p>
 * <li>注解开启使用STOMP协议来传输基于代理(message broker)的消息,</li>
 * <li>这时控制器支持使用 @MessageMapping,就像使用 @RequestMapping一样</li>
 * @author cc
 *
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer 

    /** <p>启动简单Broker</p>
     * <p>表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息</p>
     * <p>代理的名字:都是自定义的</p>
     *
     * /user     点对点(默认也是/user,可以自定义,但是必须和setUserDestinationPrefix中的设置一致)
     * /topic1   广播模式1
     * /topic2   广播模式2
     *
     * /mass     广播模式:群发
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) 
        registry.enableSimpleBroker(
                "/user", "/topic1", "/topic2", "/mass"
        );
        // 点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是 /user/
        // 注意,这里必须和上面设置的Broker:/user 一致(两个都可以自定义,但必须一致)。否则连接不上
        registry.setUserDestinationPrefix("/user/");
        // 指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀
//        registry.setApplicationDestinationPrefixes("/socket");
    

    /**
     * 这个方法的作用是添加一个服务端点,来接收客户端的连接。
     * registry.addEndpoint("/socket")表示添加了一个/socket端点,客户端(前端)就可以通过这个端点来进行连接。
     * withSockJS()的作用是开启SockJS支持。
     * @param registry registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) 
        // 注册一个STOMP的endpoint端点,并指定使用SockJS协议
        // 前端使用这个地址连接后端 WebSocket接口
        registry.addEndpoint("/broadcast", "/point")
                // 允许所有源跨域。还可以指定ip配置:http://ip:*
                // 低版本的SpringBoot(2.1.5.RELEASE 就不行)不行
                .setAllowedOriginPatterns("*")
                .withSockJS();
    


3、启动类,配置定时器

  • @EnableScheduling
@SpringBootApplication
@EnableScheduling
public class WebSocketDemoApplication 
    public static void main(String[] args) 
        SpringApplication.run(WebSocketDemoApplication.class, args);
    

二、前端基础配置

    let socket1 = new SockJS(\'http://服务器ip:服务器端口/broadcast\');
   
    let stompClient1 = Stomp.over(socket1);//广播模式

    stompClient1.connect(, (frame) => 
      stompClient1.subscribe(\'/topic1/\', (message) => 
        console.log(message.body);
      );
    );

三、后端不接收,只发送

  • 使用spring Scheduled 定时发送消息

  • 直接使用:SimpMessagingTemplate 的 convertAndSend广播模式 和 convertAndSendToUser点对点模式

1、后端

  • 注意点对点发送:

convertAndSendToUser的默认前缀(/user)是在WebSocketConfig配置文件中配置的。

代码:

    @Resource
    private SimpMessagingTemplate simpMsgTemp;

    /** 广播推送消息1:会发送给所有连接了 topic1 这个通道的客户端。
     * topic1:在Broker中配置
     **/
    @Scheduled(cron = "0/1 * * * * ?")
    public void getSocket()
        String msg = String.format("%s 的第 %s 个消息", "topic1", LocalDateTime.now().getSecond());
        log.info("",msg);
        simpMsgTemp.convertAndSend("/topic1/", msg);
    

    /** 广播推送消息2:多指定一个uri。相当于另一条通道(推荐使用)
     * <li>自定义url后缀,还可以实现用户和用户单点发送。</li>
     * topic2:在Broker中配置
     * custom:是自定义的
     */
    @Scheduled(cron = "0/1 * * * * ?")
    public void getSocketUser()
        String msg = String.format("topic2 的第 %s 个消息", LocalDateTime.now().getSecond());
        log.info("",msg);
        simpMsgTemp.convertAndSend("/topic2/custom" ,msg);
    

    /**点对点发送 convertAndSendToUser(第一个参数:一般是用户id)
     *  -> 假如用户id是1。用用户id是1的在两个地方登陆了客户端(比如不同的浏览器登陆同一个用户),
     *  -> convertAndSendToUser会把消息发送到用户1登陆的两个客户端中
     * 发送到:/user/userId/cs 下。cs是自定义的,且必须自定义一个。
     */
    @Scheduled(cron = "0/1 * * * * ?")
    public void pointToPoint()
        //这个用户id是后端获取的当前登陆的用户id
        String userId = "123";
        String msg = String.format("点对点:第 %s 个消息。用户id:%s", LocalDateTime.now().getSecond(), userId);
        log.info("",msg);
        //发送
        simpMsgTemp.convertAndSendToUser(userId,"/cs/" ,msg);
    

2、前端

  • 注意点对点的接收方式,用户id需要取出前端存的用户id
    //这样才能同时接收后端来的三套不同通道的消息。
    // broadcast 和后端:registerStompEndpoints中的配置必须一致
    // point     和后端:registerStompEndpoints中的配置必须一致
    // broadcast、point 也可以只用一个,这里只是为了好区分。
    let socket1 = new SockJS(\'http://172.16.8.1:8099/broadcast\');
    let socket2 = new SockJS(\'http://172.16.8.1:8099/broadcast\');
    let socket3 = new SockJS(\'http://172.16.8.1:8099/point\');
    // console.log("wb:" + socket)

    let stompClient1 = Stomp.over(socket1);
    let stompClient2 = Stomp.over(socket2);
    let stompClient3 = Stomp.over(socket3);

    // ----------------广播模式1--------------------
    stompClient1.connect(, (frame) => 
      console.log(\'-----------frame1\', frame)
      stompClient1.subscribe(\'/topic1/\', (message) => 
        console.log(message.body);
        this.msg = message.body;
        // console.log(JSON.parse(message.body));
      );
    );

    // ----------------广播模式2--------------------
    stompClient2.connect(, (frame) => 
      console.log(\'-----------frame2\', frame)
      stompClient2.subscribe(\'/topic2/custom\', (message) => 
        console.log(message.body);
        this.user = message.body;
        // console.log(JSON.parse(message.body));
      );
    );

    // ----------------点对点模式--------------------
    //前端获取的 userId
    let userId = \'123\';
    //连接WebSocket服务端
    stompClient3.connect(,(frame) => 
      console.log(\'Connected:\' + frame);
      stompClient3.subscribe(\'/user/\' + userId + \'/cs/\',
          (response) => 
            this.peer = response.body;
      );
    );

四、后端接收、接收后再发送

  • 也可以只接收消息,不发送。看业务需求。
  • 使用 @MessageMapping 接收前端发送过来的消息
  • 使用:@SendTo 广播模式、@SendToUser 点对点模式
  • 使用:SimpMessagingTemplate 的 convertAndSend广播模式 和 convertAndSendToUser 点对点模式

1、后端

    @Resource
    private SimpMessagingTemplate simpMsgTemp;

    /** <p>广播模式一、接收前端的消息,处理后给前端返回一个消息。</p>
     * <li>后端 把消息处理后 发送到 /mass/getResponse 路径下</li>
     * <ol>
     *     <li>@MessageMapping("/massRequest1") :作用:接收前端来的消息。类似于@RestController</li>
     *     <li>@SendTo("/mass/getResponse1"):作用跟convertAndSend类似,广播发给与该通道相连的客户端。SendTo 发送至 Broker 下的指定订阅路径 </li>
     *     <li>@SendToUser("/mass/getResponse1"):作用跟convertAndSendToUser类似,定点发送。SendTo 发送至 Broker 下的指定订阅路径 </li>
     *     <li>/mass 必须在配置文件配置</li>
     *     <li>/getResponse1 自定义的后缀</li>
     * </ol>
     */
    @MessageMapping("/massRequest1")
    @SendTo("/mass/getResponse1")
    public String mass1(String chatRoomRequest)
        //处理前端消息……
        log.info("前端消息:",chatRoomRequest);
        //返回消息
        return "@SendTo 广播一(单次) 后端处理完成!";
    

    /** 广播模式二、接收前端的消息,可以多次给前端发消息
     * <li>/mass 必须在配置文件配置</li>
     * <li>/getResponse2 自定义的后缀</li>
     */
    @MessageMapping("/massRequest2")
    public void mass2(String chatRoomRequest)
        log.info("前端的消息:",chatRoomRequest);

        for (int i = 0; i < 5; i++) 
            String msg = "后端处理后的 广播二(多次):" + i;
            simpMsgTemp.convertAndSend("/mass/getResponse2", msg);
        
        simpMsgTemp.convertAndSend("/mass/getResponse2",
                "后端处理后的 广播二(多次),后端处理完成!");
    

    /** <p>点对点一、接收前端消息,只能返回一次消息(必须登陆系统才能使用。)</p>
     * <li>只有发送原始消息的客户端才会收到响应消息,而不是所有连接的客户端都会收到响应消息。</li>
     * <li>/alone/getResponse1:自定义的,不需要在配置文件配置。</li>
     *
     * <p>@SendToUser</p>
     * <li>默认该注解前缀为 /user</li>
     * <li>broadcast属性,表明是否广播。就是当有同一个用户登录多个session时,是否都能收到。取值true/false.</li>
     *
     * @param principal Principal :登陆用户的信息,需要使用spring s安全框架写入信息?
     */
    @MessageMapping("/aloneRequest1")
    @SendToUser("/alone/getResponse1")
    public String alone1(String chatRoomRequest)
        //处理前端消息……
        log.info("前端消息:",chatRoomRequest);
        //返回消息
        return "@SendToUser 点对点一(单次) 后端处理完成!";
    

    /** 点对点二、接收前端消息,可以多次给前端发消息
     * <li>convertAndSendToUser —— 发送消息给指定用户id的</li>
     * <li>如果用户1在两个地方(A/B)登陆可以客户端,并且连接了该通道,其中一个如A给后端发消息,后端返回消息,A/B两个地方都会收到消息</li>
     * <ol>
     *     <li>@MessageMapping("/aloneRequest2") 接收前端指定用户消息,</li>
     *     <li>/alone/getResponse2 不用在配置文件中配置</li>
     *     <li>返回消息 发送到 user/userId/alone/getResponse2 下 (定点发送)</li>
     * </ol>
     */
    @MessageMapping("/aloneRequest2")
    public void alone2(String chatRoomRequest)
        //后端获取的当前登陆的用户的id(和前端一致)
        String userId = "456";
        log.info("前端的消息:",chatRoomRequest);

        for (int i = 0; i < 5; i++) 
            String msg = "后端处理后的 点对点二(多次):" + i;
            simpMsgTemp.convertAndSendToUser(userId,"/alone/getResponse2", msg);
        
        simpMsgTemp.convertAndSendToUser(userId,"/alone/getResponse2",
                "后端处理后的 点对点二(多次),后端处理完成!");
    

2、前端

  • 3点对点一 未实现。
    //连接SockJS的 broadcast
    let socket1 = new SockJS(\'http://172.16.8.7:8099/broadcast\');
    let socket2 = new SockJS(\'http://172.16.8.7:8099/broadcast\');
    let socket3 = new SockJS(\'http://172.16.8.7:8099/point\');
    let socket4 = new SockJS(\'http://172.16.8.7:8099/point\');
    //使用STMOP子协议的WebSocket客户端
    let stompClient1 = Stomp.over(socket1);
    let stompClient2 = Stomp.over(socket2);
    let stompClient3 = Stomp.over(socket3);
    let stompClient4 = Stomp.over(socket4);

    //1广播模式一
    stompClient1.connect(,(frame) => 
      console.log(\'广播模式一:\' + frame);
      //1发送消息
      stompClient1.send("/massRequest1",,"我是前端来 广播模式一 的消息!");

      //2接收消息
      stompClient1.subscribe(\'/mass/getResponse1\',(response) => 
        this.broadcast1 = response.body
      );
    );

    //2广播模式二
    stompClient2.connect(,(frame) => 
      console.log(\'广播模式二:\' + frame);
      //1发送消息
      stompClient2.send("/massRequest2",,"我是前端来 广播模式二 的消息");

      //2接收消息
      stompClient2.subscribe(\'/mass/getResponse2\',(response) => 
        this.broadcast2 = response.body
      );
    );

    //3点对点一 :必须登陆系统才能实现。要往:Principal设置用户登陆信息才行
    //1发送消息
    // stompClient3.send("/aloneRequest1",,"我是前端来 点对点一 的消息");
    stompClient3.connect(,(frame) => 
      console.log(\'点对点一1:\' + frame);
      stompClient3.send("/aloneRequest1",,"我是前端来 点对点一 的消息");
      //2接收消息
      stompClient3.subscribe(\'/user/alone/getResponse1\' ,(response) => 
        console.log(\'-------response.body\', response.body)
        this.point1 = response.body
      );
    );

    //4点对点二:必须获取现在登陆了的用户id,且必须和后端一致才行。
    stompClient4.connect(,(frame) => 
      console.log(\'点对点二:\' + frame);
      //1发送消息
      stompClient4.send("/aloneRequest2",,"我是前端来 点对点二 的消息");

      //2接收消息
      //前端获取的当前登陆的用户userId(和后端一致)
      let userId = \'456\';
      stompClient4.subscribe(\'/user/\'+userId+\'/alone/getResponse2\',(response) => 
        this.point2 = response.body
      );
    );

SpringBoot + WebSocket实现简易聊天室

①:什么是 WebSocket?

? WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议

? 根据这个定义有两个注意的地方:

1. 什么是协议?

? 协议就是相互通信的计算机双方必须共同遵守的一组约定。

2. WebSocket 协议和HTTP协议的区别?

? 1)HTTP协议基于 TCP 协议,建立链接必须通过三次握手才能发送信息。

? 2)http链接分为短链接,长链接,短链接是每次请求都要三次握手才能发送自己的信息。即每一个request 对应一个 response。长链接是在一定的期限内保持链接。保持TCP连接不断开。客户端与服务器通信,必须要有客户端发起然后服务器返回结果。客户端是主动的,服务器是被动的。

? 3)WebSocket 他是为了解决客户端发起多个 http 请求到服务器资源浏览器必须要经过长时间的轮训问题而生的,他实现了多路复用,他是全双工通信。在 webSocket 协议下客服端和浏览器可以同时发送信息。

②:使用基于STOMP协议的WebSocket+Springboot实现简易聊天室
1. 编写配置文件
@Configuration
@EnableWebSocketMessageBroker   //通过此注解开启 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //设置消息代理的前缀
        // 如果消息的前缀是 /topic 就会将消息转发给消息代理(broker)再由消息代理转发给所有连接的客户端
        config.enableSimpleBroker("/topic");    //客户端接收服务端消息的地址前缀

        //配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。
        // 例如前缀为"/app"的 destination 可以通过 @MessageMapping 注解的方法处理,而其他 destination("/topic","/query") 将被直接交给 broker 处理
        config.setApplicationDestinationPrefixes("/app");   //客户端给服务端发消息的地址前缀
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。
        // sockJs 可以解决对 WebSocket 的兼容性问题,客户端将通过这里配置的 url 建立 WebSocket 连接
        registry.addEndpoint("/chat").withSockJS();
    }
}
2. 编写控制器
@Controller
public class GreetingController {

    /**
     * 执行步骤:
     *  1,由 WebSocketConfig 中的配置,@MessageMapping 注解接收 "/app/hello" 路径发来的消息
     *  2,注解方法对消息进行处理后,将消息转发到 @SendTo 定义的路径上
     *  3,@SendTo 定义的路径是一个前缀为 "/topic" 的路径,由配置文件,此消息将被交给消息代理 broker,由 broker 进行广播
     * @param message
     * @return
     */
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) {
        return message;
    }
}
3.html代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
    <div>
        <label for="name">请输入用户名</label>
        <input type="text" id="name" placeholder="用户名">
    </div>
    <div>
        <button id="connect"type="button">连接</button>
        <button id="disconnect"type="button" disabled="disabled">断开连接</button>
    </div>
    <div id="chat" style="display: none;">
        <div>
            <label for="name">请输入聊天内容</label>
            <input type="text" id="content" placeholder="聊天内容">
        </div>
        <button id="send" type="button">发送</button>
        <div id="greetings">
            <div id="conversation" style="display: none">群聊进行中</div>
        </div>
    </div>
</body>
</html>
4. js文件
var stompClient = null;
//页面显示设置
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
        $("#chat").show();
    } else {
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greeting").html("");
}

//建立一个 WebSocket 连接,建立连接之前必须输入用户名
function connect() {
    if (!$("#name").val()) {
        return;
    }
    //创建一个 SockeJS 实例
    var socket = new SockJS(‘/chat‘);
    //使用stomp.over方式创建一个stompClient,完成客户端的创建。
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        //进行页面设置
        setConnected(true);
        //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展示出来
        stompClient.subscribe(‘/topic/greetings‘, function (greetings) {
            showGreeting(JSON.parse(greetings.body));
        })
    })
}

//断开 WebSocket 连接
function disconnect() {
    if (stompClient != null) {
        stompClient.disconnect();
    }
    setConnected(false)
}

//发送信息
function sendName() {
    stompClient.send("/app/hello", {},
        JSON.stringify({‘name‘: $(‘#name‘).val(), ‘content‘: $(‘#content‘).val()}));
}

//展示信息
function showGreeting(message) {
    $(‘#greetings‘)
        .append("<div>" + message.name + ":" + message.content + "</div>")
}

$(function () {
    //建立连接
    $("#connect").click(function () {
        connect();
    });
    //断开连接
    $("#disconnect").click(function () {
        disconnect();
    });
    //发送信息
    $("#send").click(function () {
        sendName();
    });
})
5.注意事项

? 1)maven 引入依赖错误(尽量去 maven 的中央仓库拷贝依赖)

? 2)stomp 协议的引入

    使用STOMP的好处在于,它完全就是一种消息队列模式,你可以使用生产者与消费者的思想来认识它,发送消息的是生产者,接收消息的是消费者。而消费者可以通过订阅不同的destination,来获得不同的推送消息,不需要开发人员去管理这些订阅与推送目的地之前的关系。

? 案例见spring官网就有一个简单的spring-boot的stomp-demo,如果是基于springboot,大家可以根据spring上面的教程试着去写一个简单的demo。

③:换种方式实现群发消息
控制器
    /**
     *  1. @MessageMapping("/hello") Spring提供一个 @MessageMapping 注解实现了对 WebScoket 的封装
     *  2. SimpMessagingTemplate 是 Spring-WebSocket 内置的一个消息发送的工具
     * @param message
     * @throws Exception
     */
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
    @MessageMapping("/hello")
    public void  greeting(Message message) throws Exception{
        //使用这个方法进行消息的转发发送
        simpMessagingTemplate.convertAndSend("/topic/greetings",message);
    }
④:实现点对点通信

? 刚刚实现的功能是群发消息,下面看下私聊是如何实现的。点对点通信需要配置多个用户,我们用 SpringSecurity 添加两个用户。

1. 添加 SpringSecurity 依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
2. SpringSecurity 配置文件
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                //添加两个用户 admin,sang,密码设为123。
                .withUser("admin")
                .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
                .roles("admin")
                .and()
                .withUser("sang")
                .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
}
3. 修改 WebSocket 配置文件
@Configuration
@EnableWebSocketMessageBroker   //通过此注解开启 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {

        //客户端接收服务端消息的地址前缀
        //在群发的基础上,添加一个客户端接收地址的前缀。
        config.enableSimpleBroker("/topic","/queue");   

        //客户端给服务端发消息的地址前缀
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。
        // sockJs 可以解决对 WebSocket 的兼容性问题,客户端将通过这里配置的 url 建立 WebSocket 连接
        registry.addEndpoint("/chat").withSockJS();
    }
}
4. 修改控制器
@Controller
public class GreetingController {

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    //群发消息使用 @SendTo 注解
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) throws Exception{
        return message;
    }

    //点对点发送消息使用 SimpMessagingTemplate 实现
    @MessageMapping("/chat")    //来自 "/app/chat" 的消息将会被此方法处理
    public void chat(Principal principal, Chat chat)throws Exception{
        String from = principal.getName();
        chat.setFrom(from);
        simpMessagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat",chat);
    }

}
5. onlinechat.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/chat.js"></script>
</head>
<body>

    <div>
        <div id="chatsContent"></div>
        <div>
            请输入聊天内容:<input type="text" id="content" placeholder="聊天内容">
            目标用户:<input type="text" id="to" placeholder="目标用户">
            <button type="button" id="send">发送</button>
        </div>
    </div>

</body>
</html>
6. chat.js
var stompClient = null;

//建立一个 WebSocket 连接,建立连接之前必须输入用户名
function connect() {

    //创建一个 SockeJS 实例
    var socket = new SockJS(‘/chat‘);
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展示出来
        stompClient.subscribe(‘/user/queue/chat‘, function (chat) {
            showGreeting(JSON.parse(chat.body));
        })
    })
}

//发送信息
function sendMsg() {
    stompClient.send("/app/chat", {},
        JSON.stringify({‘content‘: $(‘#content‘).val(), ‘to‘: $(‘#to‘).val()}));
}

//展示信息
function showGreeting(message) {
    $(‘#chatsContent‘)
        .append("<div>" + message.from + ":" + message.content + "</div>")
}

$(function () {
   connect();
   $(‘#send‘).click(function () {
       sendMsg();
   });
})
7. 目录结构及演示效果

演示效果时请使用不同用户登录的同一浏览器或者不同浏览器演示

技术图片
技术图片
技术图片

以上是关于SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息的主要内容,如果未能解决你的问题,请参考以下文章

springboot整合websocket实现客户端与服务端通信

SpringBoot及SpringCloud实现webSocket群发及单点发送

SpringBoot+Vue+Websocket 实现服务器端向客户端主动发送消息

Websocket断线重连怎么实现的

C# websocket与html js实现文件发送与接收处理

Spring Boot2 系列教程 (十七) | 整合 WebSocket 实现聊天室