spring boot+vue实现stomp协议

一、后端
1、安装依赖

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

2、定义消息实体类(根据特定业务来定义)

@Data
@AllArgsConstructor
public class ChatMessage {
    String sender;
    String message;
}

3、定义配置文件类

package com.example.stomptest.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    /**
     * 注册Stomp服务端点
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // addEndpoint 设置与客户端建立连接的url
        registry.addEndpoint("/ws")
                // 设置允许跨域
                .setAllowedOriginPatterns("*")
                // 允许SocketJs使用,是为了防止某些浏览器客户端不支持websocket协议的降级策略
                .withSockJS();
    }

    /**
     * 配置消息代理
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 客户端发送消息的请求前缀
        registry.setApplicationDestinationPrefixes("/app");

        // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送
        registry.enableSimpleBroker("/topic", "/queue");

        // 服务端通知客户端的前缀,可以不设置,默认为user
        registry.setUserDestinationPrefix("/user");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
//                log.info("--websocket信息发送前--");
                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                if (accessor != null) {
                    // 判断是否是连接Command 如果是,需要获取token对象
                    if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                        String token = accessor.getFirstNativeHeader("token");//下方可执行解析token逻辑
                        log.info("toekn信息:"+token);
//                        final String token = accessor.getFirstNativeHeader(Constant.HEADER_TOKEN);
//                        if (!TokenUtil.validateToken(token)) {
//                            return null;
//                        }
//                        final LoginUser user = TokenUtil.getUserFromToken(token);
//                        UserUtil.setUser(user);
                        // sendToUser 需要与这里的user获取的principal一样
//                        accessor.setUser(new SocketUser(user));
                        accessor.setUser(() -> "test");
                        log.info("websocket 连接成功");
                    }
                }
                return message;
            }
        });
    }
}

4、定义监视器

package com.example.stomptest.handle;

import com.example.stomptest.entity.ChatMessage;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Component
@Slf4j
public class WebSocketEventListener {
    @Resource
    private SimpMessageSendingOperations messagingTemplate;

    /**
     * 连接建立事件
     * @param event
     */
    @EventListener
    public void handleWebSocketConnectListener(SessionConnectEvent event) {
        log.info("建立一个新的连接");
    }


    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {

//        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
//
//        String username = (String) headerAccessor.getSessionAttributes().get("login");
        log.info("用户断开连接");
    }
}

5、定义控制器测试用

package com.example.stomptest.controller;


import com.example.stomptest.entity.ChatMessage;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

@RestController
@Slf4j
public class ChatController {
    @Resource
    private SimpMessagingTemplate messagingTemplate;


    @MessageMapping("/sendToAll")
//    @SendTo("/topic/notice")
    public String sendToAll(String message) {
        String result="服务端通知: " + message;
        messagingTemplate.convertAndSend("/topic/notice",result);
        return result;
    }

    /**
     * 点对点发送消息
     * <p>
     * 模拟 张三 给 李四 发送消息场景
     * @param username  接收消息的用户
     * @param message   消息内容
     */
    @MessageMapping("/sendToUser/{username}")
    public void sendToUser(@DestinationVariable String username, String message) {
        String sender="1910";
        String receiver = username; // 接收人

        log.info("发送人:{}; 接收人:{}", sender, receiver);
        // 发送消息给指定用户 /user/{username}/queue/greeting
        messagingTemplate.convertAndSendToUser(receiver, "/queue/message", new ChatMessage(sender, message));
    }

}

二、前端
1、安装依赖

npm i stompjs -S

npm i sockjs-client -S

2、测试代码

<template>
  <el-button type="primary" @click="startSub">订阅广播</el-button>
  <el-input v-model="input" placeholder="Please input" />
  <el-button type="primary" @click="sendMessage">群发消息</el-button>
  <el-button type="primary" @click="subscribeMessagePoint">点对点订阅消息</el-button>
  <el-button type="primary" @click="sendMessagePoint">点对点发消息</el-button>
  <el-button type="primary" @click="connect">建立连接</el-button>
  <el-button type="primary" @click="disconnect">关闭连接</el-button>
</template>

<script setup>
import { ref, onMounted, onBeforeMount, onBeforeUnmount, nextTick, reactive, watch } from 'vue';
import SockJS from 'sockjs-client/dist/sockjs.min.js';
import Stomp from 'stompjs';

const stompClient = ref(null);
const input = ref('');

onMounted(() => {
  connect();
});

const connect = () => {
  let socket = new SockJS('http://localhost:3301/ws');

  stompClient.value = Stomp.over(socket);

  stompClient.value.connect(
    {
      login: 'test',//对于点对点通信必不可少
      token: '3224sdsdfgdfdfsfddfsf',
    },
    () => {
      console.log('连接成功');
    },
    (error) => {
      console.log('连接失败: ' + error);
    }
  );
};

const disconnect = () => {
  if (stompClient.value && stompClient.value.connected) {
    stompClient.value.disconnect(() => {
      console.log('断开连接');
    });
  }
};

const startSub = () => {
  //订阅广播消息
  stompClient.value.subscribe('/topic/notice', (res) => {
    console.log('订阅广播成功:' + res.body);
  });
};

const sendMessage = () => {
  //广播发布消息
  stompClient.value.send('/app/sendToAll', {}, input.value);
};

const subscribeMessagePoint = () => {
  //订阅用户消息
  stompClient.value.subscribe('/user/queue/message', (res) => {
    let response = JSON.parse(res.body);
    console.log('订阅点对点成功:');
    console.log(response);
    console.log(response.message);
  });
};

const sendMessagePoint = () => {
  //给指定用户发送消息
  stompClient.value.send('/app/sendToUser/test', {}, input.value);
};
</script>

<style lang="scss" scoped></style>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
这是一个简单的Spring Boot + Vue.js实现聊天功能的代码示例,使用WebSocket实现实时通信。 后端代码: ``` // ChatMessage.java public class ChatMessage { private String sender; private String content; // getters and setters } // ChatController.java @RestController public class ChatController { @Autowired private SimpMessagingTemplate messagingTemplate; @MessageMapping("/chat") public void sendMessage(@Payload ChatMessage message) { messagingTemplate.convertAndSend("/topic/messages", message); } } // WebSocketConfig.java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/chat").withSockJS(); } } ``` 前端代码: ``` <!-- Chat.vue --> <template> <div> <input v-model="message" @keyup.enter="sendMessage"> <ul> <li v-for="message in messages">{{ message }}</li> </ul> </div> </template> <script> import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; export default { data() { return { message: '', messages: [] }; }, mounted() { const socket = new SockJS('/chat'); const stompClient = Stomp.over(socket); stompClient.connect({}, frame => { console.log('Connected: ' + frame); stompClient.subscribe('/topic/messages', message => { this.messages.push(JSON.parse(message.body)); }); }); }, methods: { sendMessage() { const message = { sender: 'me', content: this.message }; this.messages.push(message); const socket = new SockJS('/chat'); const stompClient = Stomp.over(socket); stompClient.connect({}, frame => { console.log('Connected: ' + frame); stompClient.send('/app/chat', {}, JSON.stringify(message)); }); this.message = ''; } } }; </script> ``` 这个示例使用了SockJS和Stomp.js来与服务器进行WebSocket通信。在mounted()函数中创建了WebSocket连接,并订阅了/topic/messages主题,以便接收服务器发送的消息。sendMessage()方法将用户输入的消息发送到服务器,并将其添加到消息列表中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值