SpringBoot+Vue 整合websocket实现简单聊天窗口

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

效果图

1 输入临时名字充当账号使用
SpringBoot+Vue 整合websocket实现简单聊天窗口,spring boot,vue.js,websocket

2 进入聊天窗口
SpringBoot+Vue 整合websocket实现简单聊天窗口,spring boot,vue.js,websocket

3 发送消息 (复制一个页面,输入其他名字,方便展示效果)
SpringBoot+Vue 整合websocket实现简单聊天窗口,spring boot,vue.js,websocket

4 其他窗口效果
SpringBoot+Vue 整合websocket实现简单聊天窗口,spring boot,vue.js,websocket

代码实现

后端SpringBoot项目,自行创建

pom依赖

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.7.12</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.23</version>
        </dependency>

WebSocketConfig.java

package com.dark.wsdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * WebSocket配置类。开启WebSocket的支持
 */
@Configuration
public class WebSocketConfig {

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

}

WebSocketServer.java

package com.dark.wsdemo.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dark.wsdemo.vo.MessageVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket的操作类
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{name}")
public class WebSocketServer {

    /**
     * 静态变量,用来记录当前在线连接数,线程安全的类。
     */
    private static final AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

    /**
     * 存放所有在线的客户端
     */
    private static final Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();

    /**
     * 连接 name 和连接会话
     */
    private String name;

    @OnOpen
    public void onOpen(@PathParam("name") String name, Session session) {
        /**
         * session.getId():当前session会话会自动生成一个id,从0开始累加的。
         */
        Session beforeSession = onlineSessionClientMap.get(name);
        if (beforeSession != null) {
            //在线数减1
            onlineSessionClientCount.decrementAndGet();
            log.info("连接已存在,关闭之前的连接 ==> session_id = {}, name = {}。", beforeSession.getId(), name);
            //通知之前其他地方连接被挤掉
            sendToOne(name, "您的账号在其他地方登录,您被迫下线。");
            // 从 Map中移除
            onlineSessionClientMap.remove(name);
            //关闭之前的连接
            try {
                beforeSession.close();
            } catch (Exception e) {
                log.error("关闭之前的连接异常,异常信息为:{}", e.getMessage());
            }
        }
        log.info("连接建立中 ==> session_id = {}, name = {}", session.getId(), name);
        onlineSessionClientMap.put(name, session);

        //在线数加1
        onlineSessionClientCount.incrementAndGet();
        this.name = name;
        sendToOne(name, "连接成功");
        log.info("连接建立成功,当前在线数为:{} ==> 开始监听新连接:session_id = {}, name = {}。", onlineSessionClientCount, session.getId(), name);
    }


    @OnClose
    public void onClose(@PathParam("name") String name, Session session) {
        if (name == null || name.equals("")) {
            name = this.name;
        }
        // 从 Map中移除
        onlineSessionClientMap.remove(name);

        //在线数减1
        onlineSessionClientCount.decrementAndGet();
        log.info("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:session_id = {}, name = {}。", onlineSessionClientCount, session.getId(), name);
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        JSONObject jsonObject = JSON.parseObject(message);
        String toname = jsonObject.getString("name");
        String msg = jsonObject.getString("message");
        log.info("服务端收到客户端消息 ==> fromname = {}, toname = {}, message = {}", name, toname, message);

        /**
         * 模拟约定:如果未指定name信息,则群发,否则就单独发送
         */
        if (toname == null || toname == "" || "".equalsIgnoreCase(toname)) {
            sendToAll(msg);
        } else {
            sendToOne(toname, msg);
        }
    }

    /**
     * 发生错误调用的方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误,错误信息为:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息
     */
    private void sendToAll(String message) {
        // 遍历在线map集合
        onlineSessionClientMap.forEach((onlineName, toSession) -> {
            // 排除掉自己
            if (!name.equalsIgnoreCase(onlineName)) {
                log.info("服务端给客户端群发消息 ==> name = {}, toname = {}, message = {}", name, onlineName, message);
                MessageVo messageVo = new MessageVo();
                messageVo.setFrom(name);
                messageVo.setDate(new Date());
                messageVo.setMessage(message);
                toSession.getAsyncRemote().sendText(JSON.toJSONString(messageVo));
            }
        });
    }

    /**
     * 指定发送消息
     *
     * @param toName
     * @param message
     */
    private void sendToOne(String toName, String message) {
        // 通过name查询map中是否存在
        Session toSession = onlineSessionClientMap.get(toName);
        if (toSession == null) {
            log.error("服务端给客户端发送消息 ==> toname = {} 不存在, message = {}", toName, message);
            return;
        }
        // 异步发送
        log.info("服务端给客户端发送消息 ==> toname = {}, message = {}", toName, message);
        MessageVo messageVo = new MessageVo();
        messageVo.setFrom(name);
        messageVo.setDate(new Date());
        messageVo.setMessage(message);
        toSession.getAsyncRemote().sendText(JSON.toJSONString(messageVo));

    }

}

MessageVo.java

package com.dark.wsdemo.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

@Data
public class MessageVo {
    private String from;
    //json时候格式化为时间格式
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date date;
    private String message;
}

Vue代码实现

App.vue文章来源地址https://www.toymoban.com/news/detail-707926.html

<template>
  <div id="app">
    <!-- Modal Dialog -->
    <div class="modal" v-if="!username">
      <div class="modal-content">
        <h2>请输入你的名字</h2>
        <input type="text" v-model="inputUsername" />
        <button @click="setUsername">确定</button>
      </div>
    </div>

    <!-- Chat Box -->
    <div class="chat-box" v-if="username">
      <div class="chat-history">
        <div v-for="msg in messages" :key="msg.id" :class="[msg.type, 'message']">
          <div class="info">
            <span class="from">{{ msg.from }}</span>
            <span class="date">{{ msg.date }}</span>
          </div>
          <div class="bubble">
            {{ msg.message }}
          </div>
        </div>
      </div>
      <div class="chat-input">
        <input type="text" v-model="inputMessage" @keyup.enter="sendMessage" placeholder="请输入消息..."/>
        <button @click="sendMessage">发送</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputMessage: '',
      inputUsername: '',
      messages: [],
      username: '',
      ws: null,
    };
  },
  methods: {
    setUsername() {
      if (this.inputUsername.trim() === '') return;
      this.username = this.inputUsername.trim();
      this.ws = new WebSocket(`ws://localhost:8081/websocket/${this.username}`);

      this.ws.addEventListener('message', (event) => {
        const data = JSON.parse(event.data);
        this.messages.push({ ...data, type: 'left', id: this.messages.length });
      });
    },
    sendMessage() {
      if (this.inputMessage.trim() === '') return;
      const message = {
        from: this.username,
        date: new Date().toLocaleString(),
        message: this.inputMessage.trim(),
      };
      this.ws.send(JSON.stringify(message));
      this.messages.push({ ...message, type: 'right', id: this.messages.length });
      this.inputMessage = '';
    },
  },
};
</script>

<style>
/* Modal Styles */
.modal {
  display: flex;
  justify-content: center;
  align-items: center;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 9999;
}

.modal-content {
  background-color: #fff;
  padding: 20px;
  width: 300px;
  text-align: center;
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* Chat Box Styles */
#app {
  background-color: #f2f2f2;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  font-family: Arial, sans-serif;
}

.chat-box {
  box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
  width: 300px;
  height: 400px;
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.chat-history {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  background-color: #fff;
}

.message {
  padding: 5px 0;
}

.info {
  font-size: 12px;
  color: gray;
  margin-bottom: 4px;
}

.left .bubble {
  background-color: #e6e6e6;
  border-radius: 15px;
  padding: 12px;
  display: inline-block;
}

.right .bubble {
  background-color: #007bff;
  color: white;
  border-radius: 15px;
  padding: 12px;
  display: inline-block;
  margin-left: auto;
}

.chat-input {
  display: flex;
  padding: 10px;
  background-color: #f7f7f7;
  border-top: 1px solid #ccc;
}

input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
  margin-right: 10px;
}

button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}
</style>

到了这里,关于SpringBoot+Vue 整合websocket实现简单聊天窗口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • springboot整合tio-websocket方案实现简易聊天

    一、导包(导入TIO的两个依赖,其他必要依赖不赘述) 二、yml配置 三、配置参数 四、实现一些监听类 1.ServerAioListener监听 2.IpStatListener监听(这个可选) 3.WsServerAioListener监听 4.IWsMsgHandler拦截(里面逻辑根据具体业务,但是必须实现这个,不然启动报错) 五、一些消息体(根据

    2024年02月14日
    浏览(27)
  • springboot+websocket实现简单的聊天室

    HTML HTML是创建和构造网页的标准标记语言。它使用一组标记标签描述网页上的内容结构。HTML文档由HTML元素的嵌套结构组成,每个元素由尖括号( )括起的标签表示。这些元素定义了网页的各个部分,如标题、段落、图像、链接、表单等。 JavaScript JavaScript是一种高级、解释性

    2024年01月21日
    浏览(45)
  • Vue+Websocket<简单实现聊天功能>

    此篇文章是针对 Websocket 的简单了解和应用,利用 Nodejs 简单搭建一个服务器加以实现。 首先创建一个 vue 项目,会vue的我就不必多说了; 然后再创建一个 server 文件夹,在终端上打开该文件夹,输入 vue init (一直敲 \\\"回车\\\" 键),最后再建一个 server.js 文件,如下图 代码如下

    2023年04月22日
    浏览(32)
  • Springboot + Websocket的集成实现简单的聊天室功能

    WebSocket是一种网络通信协议,它可以在单个TCP连接上实现双向(全双工)通信。WebSocket使用HTML5标准,并且可以在客户端和服务器之间建立持久连接,这意味着连接在浏览器刷新或关闭后仍然保持打开状态。 WebSocket的主要优点包括: 1. 双向通信:WebSocket支持客户端和服务器之

    2024年03月21日
    浏览(36)
  • springboot + websocket对接文心一言接口实现简单上下文聊天(贴代码)

    如题,第一次用websocket,做了个这玩意,只做了上下文的聊天,没做流式。 中间还有个低级报错但卡了好久,具体可以看【错误记录】websocket连接失败,但后端毫无反应,还有【错误记录】ruoyi-vue@Autowired注入自定义mapper时为null解决 ,感兴趣可前往观看。 实际上我后端用的

    2024年02月07日
    浏览(38)
  • 基于 Vue3 和 WebSocket 实现的简单网页聊天应用

    一个基于Vue3和WebSocket的简易网络聊天室项目,包括服务端和客户端部分。 项目地址 websocket-chat 下面是项目的主要组成部分和功能: 项目结构 功能特性 私聊功能:用户可以选择联系人进行一对一私聊,发送即时消息。 群聊功能:用户可以加入群组,与群组成员进行群聊。

    2024年02月03日
    浏览(41)
  • SpringBoot+Netty+Vue+Websocket实现在线推送/聊天系统

    ok,那么今天的话也是带来这个非常常用的一个技术,那就是咱们完成nutty的一个应用,今天的话,我会介绍地很详细,这样的话,拿到这个博文的代码就基本上可以按照自己的想法去构建自己的一个在线应用了。比如聊天,在线消息推送之类的。其实一开始我原来的想法做在

    2024年02月03日
    浏览(32)
  • SpringBoot和Vue2集成WebSocket,实现聊天室功能

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

    2024年01月23日
    浏览(36)
  • SpringBoot+Vue整合WebSocket实现实时通讯

            在开发过程中,我们经常遇到需要对前台的列表数据,实现实时展示最新的几条数据,或者是调度的任务进度条实现实时的刷新......,而对于这种需求,无状态的http协议显然无法满足我们的需求,于是websocket协议应运而生。websocket协议本质上是一个基于tcp的协议

    2024年02月13日
    浏览(29)
  • Vue + Element-Plus + SpringBoot + WebSocket实现简易网络聊天室

    项目流程图 1. 前端搭建:         前端用Vue+Element-Plus 来搭建,由登录页面和聊天页面组成 1.1 登录页面         由一个昵称输入框组成,用户输入自己的昵称若昵称和别的用户不重复,则可进入聊天室,否则提示错误并请重新输入。         这段代码是一个Vue.js组件的

    2024年02月03日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包