SpringBoot和Vue2集成WebSocket,实现聊天室功能

SpringBoot和Vue2集成WebSocket,实现聊天室功能

1.加入依赖
2.后端建立socket服务端
3.前端建立客户端

后端

<!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
// 配置开启WebSocket
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}
/**
 * @author websocket服务
 */
@ServerEndpoint(value = "/imserver/{userId}")
@Component
public class WebSocketServer {


    private static UserService userService;

    private static RedisTemplate redisTemplate;

    public static void setUserService(ApplicationContext context){
        userService = context.getBean(UserServiceImpl.class);
        redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
    }


    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
    /**
     * 记录当前在线连接数
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    /**
     * 连接建立成功调用的方法
     */
    // 当前用户
    private UserVo userVo;
    // 连接上服务端触发的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        if (StringUtils.hasText(userId)){
            // 加入新用户
            if (sessionMap.containsKey(userId)){
                sessionMap.remove(userId);
            }
            sessionMap.put(userId, session);
            this.userVo = userService.findById(Long.valueOf(userId));
            // 统计所有在线用户
            List<UserVo> list = new LinkedList<>();
            sessionMap.forEach((userId1,session1) -> {
                UserVo userVo = userService.findById(Long.valueOf(userId1));
                list.add(userVo);
            });
            try {
                // 发送给所有在线的用户,更新在线人数
                sendAllMessage(JSON.toJSONString(list));
            } catch (Exception e) {
                e.printStackTrace();
            }
            log.info("有新用户加入,userId={}, 当前在线人数为:{}", userId, sessionMap.size());
        }

    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId) {
        sessionMap.remove(userId);
        // 统计所有在线用户
        List<UserVo> list = new LinkedList<>();
        sessionMap.forEach((userId1,session1) -> {
            UserVo userVo = userService.findById(Long.valueOf(userId1));
            list.add(userVo);
        });
        sendAllMessage(JSON.toJSONString(list));
        log.info("有一连接关闭,移除userId={}的用户session, 当前在线人数为:{}", userId, sessionMap.size());
    }
    /**
     * 收到客户端消息后调用的方法
     * 后台收到客户端发送过来的消息
     * onMessage 是一个消息的中转站
     * 接受 浏览器端 socket.send 发送过来的 json数据
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userId") String userId) {
        userVo = userService.findById(Long.valueOf(userId));
        log.info("服务端收到用户username={},id={}的消息:{}", userVo.getNickname(),userId, message);
        // 解析消息
        JSONObject jsonObject1 = JSON.parseObject(message);
        String toUserId = jsonObject1.getString("toUserId");
        String text = jsonObject1.getString("text");
        // 判断是给指定人发,还是群发
        if (StringUtils.hasText(toUserId)){
            // {"to": "admin", "text": "聊天文本"}
            Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本
            if (toSession != null) {
                // 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
                // {"from": "zhang", "text": "hello"}
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("fromUser",userVo);
                jsonObject.put("toUser",userService.findById(Long.valueOf(toUserId)));
                jsonObject.put("text",text);
                this.sendMessage(jsonObject.toJSONString(), toSession);
                log.info("发送给用户userId={},消息:{}", toUserId, jsonObject.toJSONString());
            } else {
                log.info("发送失败,未找到用户username={}的session", toUserId);
            }
        }else{
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("fromUser",userVo);
            jsonObject.put("text",text);
            this.sendAllMessage(jsonObject.toJSONString());
            // 将消息存入redis
            redisTemplate.opsForList().rightPush("messageList",jsonObject.toJSONString());
            redisTemplate.expire("messageList",60*60, TimeUnit.SECONDS); // 过期时间

            log.info("发送给所有用户,消息:{}", toUserId, jsonObject.toJSONString());

        }
    }
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }
    /**
     * 服务端发送消息给客户端
     */
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
    /**
     * 服务端发送消息给所有客户端
     */
    private void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
                session.getBasicRemote().sendText(message);

            }
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
}

// WebSocket服务类无法进行bean的注入,所以要自己调用ApplicationContext获取bean再注入

@SpringBootApplication
public class BlogApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(BlogApplication.class, args);
        WebSocketServer.setUserService(applicationContext);
    }
}

前端

<template>
  <div class="socket">
    <el-row>
      <el-col :span="6" class="online">
          <h3 style="background:linear-gradient(to left,#cae9ff,#c7d8ff);text-align:center;padding:5px;">在线用户</h3>
          <el-scrollbar style="height:280px">
          <div v-for="user in onlineUser" :key="user.id" style="padding:5px" class="onlineUser">
            <el-avatar shape="square" size="medium" :src="user.avatar"></el-avatar>
            <span style="margin-left:8px">{{user.account}}</span>
          </div>
          </el-scrollbar>
      </el-col>
      <el-col :span="17" class="container">
          <h3 style="background:linear-gradient(to left,#cae9ff,#c7d8ff);text-align:center;padding:5px;">当前用户(<span style="color:#66b1ff">{{user.account}}</span>)</h3>
          <el-scrollbar style="height:420px" ref="scroll">
          <div class="content">
            <div class="message" v-for="item,index in messageList" :key="index" style="margin-top:5px;margin-left:5px;position:relative" :style="{justifyContent: item.fromUser.id == user.id ? 'flex-end' : 'flex-start'}">
              <el-avatar shape="square" size="medium" :src="item.fromUser.avatar" :style="{order:user.id == item.fromUser.id ? '1':'0'}"></el-avatar>
              <span style="margin-left:8px;" class="text">{{item.text}}</span>
            </div>
          </div>
          </el-scrollbar>
          <div style="margin-top: 15px;" class="write">
            <el-input placeholder="请输入内容" maxlength="100" clearable v-model="text" class="input-with-select">
              <el-button slot="append" type="primary" @click="sendMessage">发送</el-button>
            </el-input>
          </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
  import config from '@/utils/config'
  import {getCacheMessage} from '@/api/socket'
  import {mapState} from 'vuex'
  export default {
    name:'lm',
    data(){
      return {
        text:'',
        onlineUser:[],
        messageList:[],
        socket:'',
        baseUrl:config.socketUrl     
      }
    },
    computed:{
      ...mapState(['user'])
    },
    methods:{
      // 连接socket
      onOpen(){
        if (typeof WebSocket == 'undefined'){
          console.log('你的浏览器不支持webSocket')
          return;
        }
        const url = this.baseUrl+this.user.id
        
        this.socket = new WebSocket(url);

        this.socket.onopen = ()=>{
          console.log('websocket打开了,'+this.user.id+'连接成功')
        }
        this.socket.onmessage = (data)=>{
          var message = JSON.parse(data.data)
          if(message.hasOwnProperty("text")){
            this.messageList.push(message)
            this.text = ''
            let scroll = this.$refs['scroll'].$refs['wrap']
            this.$nextTick(() =>{
              scroll.scrollTop = scroll.scrollHeight
            })
          }else{
            // 统计在线人数
            this.onlineUser = message
            
          }
        }
        this.socket.onclose = function(){
          console.log('断开连接成功')
        }
      },
      sendMessage(){
        const message = {
          text:this.text,
          userId:this.user.id
        }
        if(this.text == ''||this.text == null){
          this.$message.warning('请输入内容')
        }else{
          this.socket.send(JSON.stringify(message))
        }
      },
      // 初始化,缓存的消息
      getCacheMessage(){
        getCacheMessage().then(
          res => {
            const data = res.data
            for(var i in data){
              this.messageList.push(JSON.parse(data[i]))
            }
          }
        )
      }
    },
    mounted(){
      this.getCacheMessage()
      this.onOpen()
    },
    beforeDestroy(){
      this.socket.close() // 关闭socket
    }

  }
</script>

<style scoped>
  .socket{
    width: 1200px;
    height: 600px;
    padding:26px;
    background-color: #fff;
    border-radius: 5px;
  }
  .socket .online{
    background-color: #f5f5f5;
    height: 300px;
    border-radius: 5px;
    overflow: hidden;
  }
  .socket .online .onlineUser{
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;  
  }
  .socket .container{
    position: relative;
    margin-left: 26px;
    background-color: #f5f5f5;
    height: 500px;
    border-radius: 5px;
  }
  .socket .el-button--primary{
    background-color: #66b1ff;
    color: #fff;
  }
  .socket .container .content{
    min-height: 400px;
    border: 1px solid #d6d6d6;
    border-radius: 4px;
  }
  .socket .container .content .message{
    display: flex;
    align-content: flex-start;
  }
  .socket .write{
    position:absolute;
    bottom: 10px;
    width: 500px;
    right: 50%;
    transform: translateX(50%);
  }

  .socket .text{
    min-height: 24px;
    line-height: 20px;
    padding: 8px;
    font-size: 16px;
    background-color: #fff;
    border-radius: 5px;
    max-width: 280px;
    overflow-wrap:break-word;
    word-wrap:break-word;
    word-break: bread-all;
    white-space:pre-wrap;
    overflow: hidden;
  }
</style>

springboot集成websocket实现聊天室的功能。如有不足之处,还望大家斧正。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现基于 WebSocket聊天单聊和群聊,可以分为以下几个步骤: 1. 创建 SpringBoot 项目:使用 SpringBoot 创建一个后端项目,添加 WebSocket 依赖。 2. 配置 WebSocket:在 SpringBoot 项目中添加配置类,开启 WebSocket 支持,并注册 WebSocket 处理器。 3. 创建前端项目:使用 Vue 创建一个前端项目,安装 WebSocket 库。 4. 实现单聊和群聊功能:前端和后端通过 WebSocket 进行通信,前端发送消息到后端,后端将收到的消息进行处理,然后再将消息发送给前端。 下面是一个简单的示例代码: 后端代码: 1. 添加 WebSocket 依赖 ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. WebSocket 配置类 ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/ws").setAllowedOrigins("*"); } } ``` 3. WebSocket 处理器 ``` @Component public class WebSocketHandler extends TextWebSocketHandler { private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.put(session.getId(), session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session.getId()); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 处理收到的消息 String payload = message.getPayload(); JSONObject jsonObject = JSONObject.parseObject(payload); String type = jsonObject.getString("type"); String content = jsonObject.getString("content"); String from = jsonObject.getString("from"); String to = jsonObject.getString("to"); if("chat".equals(type)) { // 单聊 WebSocketSession toSession = sessions.get(to); if(toSession != null && toSession.isOpen()) { TextMessage textMessage = new TextMessage(content); toSession.sendMessage(textMessage); } } else if("group".equals(type)) { // 群聊 for(WebSocketSession session1 : sessions.values()) { if(session1.isOpen()) { TextMessage textMessage = new TextMessage(content); session1.sendMessage(textMessage); } } } } } ``` 前端代码: 1. 安装 WebSocket 库 ``` npm install --save sockjs-client npm install --save stompjs ``` 2. 连接 WebSocket ``` import SockJS from 'sockjs-client' import Stomp from 'stompjs' let stompClient = null; function connect() { const socket = new SockJS('/ws'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); stompClient.subscribe('/topic/chat', function (message) { // 接收到消息 console.log(message); }); }); } connect(); ``` 3. 发送消息 ``` function sendChatMessage() { const message = { type: 'chat', content: 'hello', from: 'user1', to: 'user2' }; stompClient.send('/app/chat', {}, JSON.stringify(message)); } ``` 以上代码仅为示例,具体实现还需要根据具体需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值