在线聊天室(Vue+Springboot+WebSocket)

这篇具有很好参考价值的文章主要介绍了在线聊天室(Vue+Springboot+WebSocket)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实现了一个简单的在线聊天室的前后端。前端用Vue实现,后端用Springboot实现。

一、项目描述

1. 整体功能描述

        在线聊天室的功能包括创建用户和显示在线用户列表、发送消息和显示消息列表、用户和消息列表实时更新这几点。以下是整体功能的活动图:

在线聊天室(Vue+Springboot+WebSocket),vue.js,spring boot,websocket

2. 实现思路

用户身份

        进入聊天室的用户需要有一个身份,为了简便,只需要一个唯一的id和一个用户名即可。用户名由用户自定义,id由服务端分配。

        客户端通过将id和用户名记录在sessionStorage来保存用户信息,而服务端通过用户的id及session来区分用户,为此,服务端需要维护一个在线用户列表,来记录用户的信息。

        如活动图所示,当用户第一次进入聊天室时,需要输入用户名,请求服务端分配id,服务端分配id后,客户端进行记录。而用户曾进入过聊天室时,可以直接从sessionStorage中获取id和用户名。

通信方式

        客户端和服务端的通信方式有两种,首先,在线聊天需要两端的全双工通信,因此需要建立websocket连接。通过websocket进行通信。

        但是,如活动图所示,websocket的连接需要用到用户id,而用户在首次进入聊天室时还没有id,此时就需要客户端直接发送http请求到服务端,来获取一个id。

实时更新

        聊天室中的在线用户列表以及消息列表需要实时更新。此功能可以通过websocket实现,每当用户进入、退出聊天室,或发送消息时,客户端会通过websocket向服务端发送消息。服务端会维护一个在线用户列表和消息列表,当收到用户消息时,就会更新这个列表,并向所有用户发送更新消息,以达到实时更新的效果。

二、具体实现

1. 前端整体布局

        因为是一个在线聊天室,所以就参考了微信聊天的页面,左侧显示在线用户列表,右侧显示消息窗口。如下图:
在线聊天室(Vue+Springboot+WebSocket),vue.js,spring boot,websocket

        如图所示,左侧用简单卡片列表的形式展示在线用户列表,右侧是消息列表,通过调整样式,使自己的消息靠右显示,别人的消息靠左显示。以下是部分代码:

<template>
  <div class="chatRoom">
    <div class="personList">
      <div class="title">
        <h1>在线聊天室</h1>
      </div>
      <!-- 在线用户列表 -->
      <div class="online-person">
        <span class="title">在线用户</span>
        <div class="person-cards-wrapper">
          <div
            class="personList"
            v-for="personInfo in personList"
            :key="personInfo.id"
          >
            <PersonCard :personInfo="personInfo"></PersonCard>
          </div>
        </div>
      </div>
    </div>
    <!-- 聊天窗口 -->
    <div class="chatContent">
      <ChatWindow></ChatWindow>
    </div>
  </div>
</template>

<!-- 聊天窗口组件 -->
<template>
  <div class="chat-window">
    <div class="bottom">
      <!-- 聊天信息列表 -->
      <div class="chat-content" ref="chatContent">
        <div class="chat-wrapper" v-for="item in chatList" :key="item.id">
          <!-- 通过uid判断是自己的消息还是别人的 -->
          <div class="chat-friend" v-if="item.uid != $store.state.id">
            <div class="chat-text">
              {{ item.msg }}
            </div>
            <div class="info-time">
              <span>{{ item.name }}</span>
              <span>{{ item.time }}</span>
            </div>
          </div>
          <div class="chat-me" v-else>
            <!-- 略 -->
          </div>
        </div>
      </div>
      <div class="chatInputs">
        <input class="inputs" v-model="inputMsg" @keyup.enter="sendText" />
        <div class="send box" @click="sendText">
          <span class="sendText">发送</span>
        </div>
      </div>
    </div>
  </div>
</template>

2. 用户身份

        用户身份的实现方式在实现思路中已经给出,这里不再说明。此外,前端除了在sessionStorage中存储用户信息,还会利用Vuex存储用户信息。

3. 前后端通信

        前后端的通信有两种方式,首先是直接发送http请求,比如用户请求分配id时,这里是采用axios的方式像后端接口发送请求。

        然后是websocket连接,前端需要始终与后端保持websocket连接。需要注意的是,建立连接的时机需要保证在用户得到id后。以下是部分代码:

 前端:

    <!-- 输入用户名的对话框 -->
    <div v-if="showDialog" class="dialog-wrapper">
      <div class="dialog">
        <h2 class="dialog-title">请输入您的昵称</h2>
        <el-input
          v-model="inputName"
          class="dialog-content"
          placeholder="请输入内容"
          maxlength="10"
        ></el-input>
        <button @click="closeDialog" class="dialog-button">确定</button>
      </div>
    </div>

<script>
  mounted() {
    // 如果sessionStorage中无用户id,则需输入用户名,分配id
    if (!sessionStorage.hasOwnProperty("id")) {
      this.openDialog();
    } else {
      // 如果sessionStorage中有,则无需分配,直接用vuex存储
      this.$store.dispatch("setUserId", sessionStorage.getItem("id"));
      this.$store.dispatch("setUserName", sessionStorage.getItem("name"));
      console.log("增加已有用户:" + this.$store.state.name);
      addUser({ id: this.$store.state.id, name: this.$store.state.name }).then(
        (res) => {
          // 建立websocket连接,要保证addUser后服务端才发送更新消息
          this.$store.dispatch("connect");
        }
      );
    }
  },

  methods: {
    openDialog() {
      this.showDialog = true;
    },
    closeDialog() {
      if (this.inputName.length == 0) {
        this.$message({
          message: "用户名不能为空",
          type: "warning",
        });
      } else {
        newUser(this.inputName).then((res) => {
          // 将获取到的id用vuex存储
          this.$store.dispatch("setUserId", res);
          this.$store.dispatch("setUserName", this.inputName);
          // 将数据存储到sessionStorage
          sessionStorage.setItem("id", res);
          sessionStorage.setItem("name", this.inputName);
          // 建立websocket连接,需要用户id, 因此在分配id后进行
          this.$store.dispatch("connect");
        });
        this.showDialog = false;
      }
    },
  },

</script>
export const newUser = (param) => {
  return axios({
    method: 'post',
    baseURL: `${baseUrl}/user/new/${param}`,
  }).then(res => res.data)
}

export const addUser = (param) =>{
  return axios({
    method: 'post',
    baseURL: `${baseUrl}/user/add`,
    data: param,
  }).then(res => res.data)
}

后端:

@RestController
@RequestMapping("user")
public class UserController {
    // 用户第一次进入,分配id
    @PostMapping("new/{name}")
    public int newUser(@PathVariable String name) {
        System.out.println(name);
        User user = new User(++Storage.cur_id, name);
        Storage.userList.put(user.getId(), user);
        return Storage.cur_id;
    }
    
    // 用户再次进入,无需分配id
    @PostMapping("add")
    public void addUser(@RequestBody User user) {
        System.out.println(user);
        Storage.userList.put(user.getId(), user);
    }
}
public class MyWebSocket {
    private Session session;

    private String userId;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static final CopyOnWriteArraySet<MyWebSocket> webSockets = new CopyOnWriteArraySet<>();
    // 用来存在线连接用户信息
    private static final ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();

    // 连接成功调用的方法
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        this.session = session;
        this.userId = userId;
        webSockets.add(this);
        sessionPool.put(userId, session);   // 存储用户的session
        // 有新用户连接后,需要通知所有用户更新在线用户列表
        sendAllMessage(JSON.toJSONString(new SocketMessage(1, Storage.getUserList(), null)));
        sendOneMessage(userId, JSON.toJSONString(new SocketMessage(2, null, Storage.getMsgList())));
    }

    // 连接关闭调用的方法
    @OnClose
    public void onClose() {
        webSockets.remove(this);
        sessionPool.remove(this.userId);
        // 将用户从在线列表中删掉,并通知所有用户
        Storage.userList.remove(Integer.parseInt(this.userId));
        sendAllMessage(JSON.toJSONString(new SocketMessage(1, Storage.getUserList(), null)));
    }

    // 收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message) {
        Message msg = JSON.parseObject(message, Message.class);
        Storage.msgList.add(msg);
        sendAllMessage(JSON.toJSONString(new SocketMessage(2, null, Storage.getMsgList())));
    }

    // 单点消息
    public void sendOneMessage(String userId, String message) {
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            log.info("【websocket消息】 单点消息:" + message);
            session.getAsyncRemote().sendText(message);
        }
    }

    // 广播消息
    public void sendAllMessage(String message) {
        for (MyWebSocket webSocket : webSockets) {
            try {
                if (webSocket.session.isOpen()) {
                    webSocket.session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}

4. 发送消息与实时更新

        聊天室需要保证用户列表与消息列表的实时更新,这里利用了Vue的响应式数据与Vuex,通过Vuex存储用户列表与消息列表,然后在Vue组件中监听它们的变化,每当有变化时,就更新自身的数据,从而使页面实时更新。

        而Vuex中数据的变化,是通过WebSocket通信实现的,实现方式在实现思路部分已经给出,这里不再说明。以下是部分代码:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const state = {
    socket: null,
    id: "",
    name: "",
    userList: [],
    msgList: [],
};

const mutations = {
    setSocket(state, socket) {
        state.socket = socket;
    },
    setId(state, id) {
        state.id = id;
    },
    setName(state, name) {
        state.name = name;
    },
    setUserList(state, userList) {
        state.userList = userList;
    },
    setMsgList(state, msgList) {
        state.msgList = msgList;
    },
}

const actions = {
    connect(context) {
        const socket = new WebSocket('ws://localhost:8089/chat/' + context.state.id)
        socket.onopen = () => {
            console.log("建立websocket连接")
        }
        socket.onmessage = (event) => {
            // 接收消息的情况有两种:更新用户列表和更新消息列表
            console.log("收到服务端的消息:" + event.data);
            let msg = JSON.parse(event.data);
            if (msg.type == '1') {
                console.log("更新用户列表: ");
                context.commit("setUserList", msg.userList);
            } else if (msg.type == '2') {
                console.log("更新消息列表");
                context.commit("setMsgList", msg.msgList);
            }
        }

        context.commit("setSocket", socket)
    },

    setUserId(context, id) {
        console.log("设置用户id为:" + id);
        context.commit("setId", id);
    },

    setUserName(context, name) {
        console.log("设置用户name为:" + name);
        context.commit("setName", name);
    },

    sendMsg(context, msg) {
        context.state.socket.send(msg);
    }
};

export default new Vuex.Store({
    actions,
    mutations,
    state,
});
export default {
  data() {
    return {
      chatList: [],
      inputMsg: "",
    };
  },
  methods: {
    //获取窗口高度并滚动至最底层
    scrollBottom() {
      this.$nextTick(() => {
        const scrollDom = this.$refs.chatContent;
        scrollDom.scrollTop = scrollDom.scrollHeight;
      });
    },
    //发送文字信息
    sendText() {
      if (this.inputMsg) {
        const now = new Date();

        const year = now.getFullYear();
        const month = ("0" + (now.getMonth() + 1)).slice(-2);
        const day = ("0" + now.getDate()).slice(-2);
        const hours = ("0" + now.getHours()).slice(-2);
        const minutes = ("0" + now.getMinutes()).slice(-2);
        const seconds = ("0" + now.getSeconds()).slice(-2);

        const formattedTime =
          year +
          "-" +
          month +
          "-" +
          day +
          "  " +
          hours +
          ":" +
          minutes +
          ":" +
          seconds;

        let msg = {
          name: this.$store.state.name,
          time: formattedTime,
          msg: this.inputMsg,
          uid: this.$store.state.id,
        };
        // 向websocket服务端发送消息
        this.$store.dispatch("sendMsg", JSON.stringify(msg));
        this.inputMsg = "";
      } else {
        this.$message({
          message: "消息不能为空",
          type: "warning",
        });
      }
    },
  },

  computed: {
    getChatMsgList() {
      return this.$store.state.msgList;
    },
  },

  watch: {
    // 更新消息列表
    getChatMsgList: {
      handler: function (newVal, oldVal) {
        console.log("更新chatList");
        this.chatList = this.getChatMsgList;
        this.scrollBottom();
      },
      deep: true,
    },
  },
};

三、代码仓库

ChatRoom: 简单的在线聊天网站的前后端文章来源地址https://www.toymoban.com/news/detail-791637.html

到了这里,关于在线聊天室(Vue+Springboot+WebSocket)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于 SpringBoot+WebSocket 无DB实现在线聊天室(附源码)

    0.1 样例展示 0.2 源码地址 GitHub:https://github.com/ShiJieCloud/web-chat Gitee:https://gitee.com/suitbaby/web-chat GitCode:I’m Jie / web-chat · GitCode 1.1 HTTP 常用的 HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出

    2024年02月05日
    浏览(38)
  • Java+Vue实现聊天室(WebSocket进阶-聊天记录)

    WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服

    2024年02月11日
    浏览(45)
  • websocket+elementui+vue实现简易聊天室

    搭建服务端环境 安装socket.io 服务端基于node,js的express框架生成,所以写成模块,引入至app.js中 其中,io.sockets.emit用于向所有建立连接的客户端发送信息,socket.broadcast.emit用于向除发送方之外的客户端发送信息。 客户端基于vue和elementui 进入聊天页面后,先判断用户是否登录,

    2024年04月25日
    浏览(40)
  • websocket实现聊天室(vue2 + node)

    需求分析如图: 搭建的项目结构如图: 前端步骤: vue create socket_demo (创建项目) views下面建立Home , Login组件 路由里面配置路径 Home组件内部开启websocket连接 前端相关组件代码: Login组件 Home组件 router/index.js 后端步骤: 在项目外层创建server文件夹(src目录同级) npm init -y创建

    2024年01月22日
    浏览(38)
  • Vue+Nodejs 使用WebSocket创建一个简易聊天室

    使用vue编写前端页面,nodejs处理服务端消息,WebSocket进行实时通信 1.客户端 2. 服务端 使用的是nodejs

    2024年02月16日
    浏览(36)
  • 【WebSocket项目实战】聊天室(前端vue3、后端spring框架)

    最近我学习了WebSocket,为了更好地掌握这一技术,我决定通过做一个项目来巩固学习成果。在这个项目中,我将使用JavaScript和WebSocket来实现实时通信,让客户端和服务器端能够实时地传递和接收数据。通过这个项目,我希望能够更深入地了解WebSocket的工作原理,并且能够在实

    2024年02月04日
    浏览(36)
  • WebSocket+Vue实现简易多人聊天室 以及 对异步调用的理解

    代码仓库:github   HTTP是不支持长连接的,WebSocket是一种通信协议,提供了在单一、长连接上进行全双工通信的方式。它被设计用于在Web浏览器和Web服务器之间实现,但也可以用于任何需要实时通信的应用程序。使用ws作为协议标识符,如果需要加密则使用wss作为协议标识符

    2024年01月17日
    浏览(52)
  • 基于WebSocket的在线文字聊天室

    与Ajax不同,WebSocket可以使服务端主动向客户发送响应,本案例就是基于WebSocket的一个在线聊天室,不过功能比较简单,只能满足文字交流。演示如下。 案例学习于b站up主,链接 。这位up主讲的非常清楚,值得去学习。本文属于记录自我学习过程的文章。 项目目录下app.js 项

    2024年02月13日
    浏览(51)
  • 【你的第一个socket应用】Vue3+Node实现一个WebSocket即时通讯聊天室

    这篇文章主要是用WebSocket技术实现一个 即时通讯聊天室 ,首先先要了解为什么使用WebSocket而不是普通的HTTP协议,如果使用HTTP协议它是下面这种情况: 我发送一条消息,发送一个发送消息的请求;* 一直轮询接收别人发送的消息,不管有没有发送都要定时去调用接口。这里明

    2023年04月20日
    浏览(51)
  • Gin+WebSocket实战——在线聊天室WebSocketDemo详细使用教程

    Github:https://github.com/palp1tate/WebsocketDemo 欢迎star!😎 利用 Gin + WebSocket 实现的在线聊天室 Demo 项目,支持加入/离开聊天室广播、给其他用户发送消息等。 进入项目根目录,执行命令 go run . 命令,结果如下: 可以看到们的 HTTP 服务已经启动成功并运行在了 8080 端口上。 接下

    2024年04月26日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包