当前位置:首页 » 《随便一记》 » 正文

SpringBoot+Vue搭建一个WebSocket的实时聊天室_m0_51510236的博客

20 人参与  2021年12月21日 12:17  分类 : 《随便一记》  评论

点击全文阅读


首先搭建一个SpringBoot项目,其中各个文件:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>love.xiaohh</groupId>
    <artifactId>websocket-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>websocket-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

love.xiaohh.websocket.WebsocketApplication,其中注册一个bean,使WebSocket的Endpoint生效

package love.xiaohh.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@EnableWebSocket // 开启websocket的注解,告诉Spring我们要使用WebSocket
@SpringBootApplication
public class WebsocketApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebsocketApplication.class, args);
    }

    /**
     * 注册一个 ServerEndpointExporter,这样可以让注册到容器当中的WebSocket的Endpoint生效
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

love.xiaohh.websocket.controllers.ws.WebSocketController,这是主角,也是websocket的控制器,有点像Controller

package love.xiaohh.websocket.controllers.ws;

import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>
 * WebSocket的控制器
 * </p>
 * @author tanghai
 * @version 1.0
 * @date 2021-09-14 星期二 11:02:09
 */
@Component
@ServerEndpoint("/default/websocket")
public class WebSocketController {

    /**
     * 当前在线的总人数
     */
    private static final AtomicLong ONLINE_COUNT = new AtomicLong(0L);

    /**
     * 所有在线的人数的
     */
    private static final Map<String, Session> ALL_ONLINE_INFO = new HashMap<String, Session>();

    /**
     * websocket被打开的方法
     *
     * @param session 会话
     */
    @OnOpen
    public void open(Session session) {
        // 获取sessionId,让后将session存入到Map中
        final String id = session.getId();
        ALL_ONLINE_INFO.put(id, session);
        // 在线人数自加1并发送给所有的客户端
        final long onlineCount = ONLINE_COUNT.incrementAndGet();
        this.sendMessage(0, onlineCount + "");
    }

    /**
     * websocket被退出
     *
     * @param session 会话
     */
    @OnClose
    public void close(Session session) {
        // 将session从缓存当中移除
        final String id = session.getId();
        ALL_ONLINE_INFO.remove(id);
        // 在线人数自减1并发送给所有的客户端
        final long onlineCount = ONLINE_COUNT.decrementAndGet();
        this.sendMessage(0, onlineCount + "");
    }

    /**
     * 当收到客户端发送过来的消息时触发
     *
     * @param message 客户端发送过来的消息
     * @param session 客户端的session会话
     */
    @OnMessage
    public void message(String message, Session session) throws IOException {
        // 将消息发送到所有的客户端
        this.sendMessage(1, message);
    }

    /**
     * 发送消息给所有用户
     *
     * @param type    消息类型;0=更新在线人数,1=发送的消息
     * @param message 消息实体
     */
    private void sendMessage(int type, String message) {
        // 拼接成JSON,让后前端可以解析
        String targetMessage = "{\"type\":" + type + ",\"message\":\"" + message + "\"}";
        // 遍历并发给所有的客户端
        ALL_ONLINE_INFO.values().forEach(session -> {
            try {
                session.getBasicRemote().sendText(targetMessage);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

后端的代码大功告成,然后我们搭建一个前端的Vue项目,搭建过程可参见Vue项目的搭建教程,搭建好后需要安装一个element-ui:

npm i element-ui -S

修改几个文件:

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>websocket-demo-ui</title>
    <style type="text/css">
      body,#app {
        margin: 0;
        border: 0;
        padding: 0;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

src/main.js

import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import router from './router'

// 引入并使用elementui
Vue.use(ElementUI)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

src/App.vue

<template>
  <router-view/>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>

</style>

src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import ChatRoom from '@/components/ChatRoom'

Vue.use(Router)

export default new Router({
  routes: [
    {
      // 根路径默认到聊天室
      path: '/',
      name: 'ChatRoom',
      component: ChatRoom
    }
  ]
})

src/components/ChatRoom.vue

<template>
  <div id="wrapper">
    <el-row style="height: 10%">
      <el-col :span="12"><h1>小海海的聊天室</h1></el-col>
      <el-col :span="12" style="text-align: right;margin: auto 20px auto 0;">当前在线人数: {{ onlineCount }}</el-col>
    </el-row>
    <el-row style="height: 80%">
      <el-col :span="24">
        <chat-item v-for="(message, index) in chatList" :customerMessage="message" :key="index"/>
      </el-col>
    </el-row>
    <el-row>
      <el-col :span="18">
        <el-input v-model="customerMessage" @keydown.enter="sendMessage" placeholder="消息内容..."></el-input>
      </el-col>
      <el-col :span="6">
        <el-button type="primary" @click="sendMessage" plain>发送</el-button>
      </el-col>
    </el-row>
  </div>
</template>

<script>
import ChatItem from './ChatItem'

export default {
  name: 'ChatRoom',
  data() {
    return {
      socketInfo: {
        path: 'ws://127.0.0.1:8080/default/websocket',
        socket: null
      },
      onlineCount: 0,
      chatList: [],
      customerMessage: ''
    }
  },

  mounted() {
    // 初始化 WebSocket
    this.init()
  },

  destroyed() {
    this.socketInfo.socket.close();
  },

  methods: {
    /**
     * 初始化WebSocket
     */
    init() {
      if (typeof (WebSocket) === 'undefined') {
        alert('您的浏览器不支持WebSocket')
      } else {
        const socket = new WebSocket(this.socketInfo.path)

        // 发送消息的事件
        socket.onmessage = this.getMessage
        // socket被打卡的事件
        socket.onopen = this.socketOpen

        this.socketInfo.socket = socket
      }
    },
    socketOpen() {
      this.chatList.push('您已经进入聊天室,可以开始聊天了!')
    },
    /**
     * 发送消息给服务端
     */
    sendMessage() {
      if (this.customerMessage === '') alert('发送消息不能为空')
      this.socketInfo.socket.send(this.customerMessage)
      this.customerMessage = ''
    },
    /**
     * 收到服务器发过来的消息
     * @param message 消息对象
     */
    getMessage(message) {
      // 将接收到的消息转换为json格式
      const messageJson = JSON.parse(message.data)
      if (messageJson.type === 0) {
        this.onlineCount = messageJson.message
      } else if (messageJson.type === 1) {
        this.chatList.push(messageJson.message)
      }
    }
  },

  components: {
    ChatItem
  }
}
</script>

<style scoped>

</style>

src/components/ChatItem.vue

<template>
<div>
  <p>{{customerMessage}}</p>
  <hr/>
</div>
</template>

<script>
export default {
  name: 'ChatItem',
  props: {
    customerMessage: String
  }
}
</script>

<style scoped>

</style>

然后我们启动这两个程序:

# 打包maven项目
mvn clean package -Dmaven.test.skip

# 在打包好的好的target目录中启动Java程序
java -jar websocket-demo-0.0.1-SNAPSHOT.jar

# 在vue项目的根路径下下载所有依赖:
npm install

然后访问http://127.0.0.1:

在这里插入图片描述

多开几个:

在这里插入图片描述

尝试发送消息发现同步接收:

在这里插入图片描述

现在你就可以叫上你们的小伙伴们一起聊天了!如果不行那么欢迎来到代码仓库克隆代码运行哦


点击全文阅读


本文链接:http://zhangshiyu.com/post/31845.html

在线  消息  客户端  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1