SpringBoot+WebSocket搭建多人在线聊天环境

一、WebSocket是什么?

WebSocket是在单个TCP连接上进行全双工通信的协议,可以在服务器和客户端之间建立双向通信通道。

WebSocket 首先与服务器建立常规 HTTP 连接,然后通过发送Upgrade标头将其升级为双向 WebSocket 连接。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

二、WebSocket的优点

1.较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于控制协议的数据包头部较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。

2.更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。

3.保持连接状态。与HTTP不同的是,WebSocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息,而HTTP请求可能需要在每个请求都携带状态信息,比如身份认证等。

4.更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。

5.可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。

6.更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

三、项目实战

1.引入依赖

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

完整的pom文件如下所示:

<?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.7.14</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>WebSocketChatDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>WebSocketChatDemo</name>
    <description>WebSocketChatDemo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!--lombok插件依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</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>

2.创建SystemWebSocketHandler类

package com.example.websocketchatdemo.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author qx
 * @date 2023/8/22
 * @des WebSocket处理类
 */
@Slf4j
@Component
public class SystemWebSocketHandler implements WebSocketHandler {

    // 存储所有客户端会话
    private static final List<WebSocketSession> sessionList = new ArrayList<>();

    /**
     * 与服务器连接成功
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("客户端成功建立连接:{}", session.getId());
        sessionList.add(session);
    }

    /**
     * 接受客户端的消息
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        // 获取客户端的消息
        String msg = message.getPayload().toString();
        log.info("接收到客户端的消息:{}", msg);
        sendMsg(msg);
    }

    /**
     * 给所有客户端发送消息
     *
     * @param msg 消息内容
     * @throws IOException
     */
    private void sendMsg(String msg) throws IOException {
        for (WebSocketSession session : sessionList) {
            if (session.isOpen()) {
                session.sendMessage(new TextMessage(msg));
            }
        }
    }

    /**
     * 通讯异常
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("通讯出现异常");
    }

    /**
     * 连接关闭
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("连接关闭");
    }

    /**
     * 是否允许分段发送
     */
    @Override
    public boolean supportsPartialMessages() {
        // 一次性发送消息
        return false;
    }
}

3.创建WebSocket配置类

这个配置类主要进行跨域的配置。

package com.example.websocketchatdemo.websocket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * @author qx
 * @date 2023/8/22
 * @des WebSocket配置类
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebMvcConfigurer, WebSocketConfigurer {

    @Autowired
    private SystemWebSocketHandler handler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 设置WebSocket服务器地址 ws://localhost:8080/SpringBootWebSocket
        registry.addHandler(handler, "/SpringBootWebSocket").setAllowedOrigins("*");
    }
}

4.前台页面编写

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>

</head>
<body>
聊天消息内容:
<br/>
<textarea id="chat_content" readonly style="height: 400px;width: 600px"></textarea>
<br/>输入框:
<br/>
<div>
    <textarea id="in_content" placeholder="请输入内容" style="height: 100px;width: 500px"></textarea>
</div>
<button type="button" id="btn_send">发送消息</button>
<br/><br/>
<label>用户:</label>
<div>
    <input type="text" id="in_name" placeholder="请输入姓名"/>
</div>
<br/>
<button type="button" id="btn_join">进入聊天室</button>
<button type="button" id="btn_quit">离开聊天室</button>
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
    $(function () {
        var socketUrl = "ws://localhost:8080/SpringBootWebSocket";
        var ws = null;
        $("#btn_join").click(function () {
            if (ws != null) {
                alert("用户[" + $("#in_name").val() + "]已加入连接");
                return;
            }
            // 判断当前浏览器是否支持WebSocket
            if ('WebSocket' in window) {
                ws = new WebSocket(socketUrl)
            } else {
                alert("当前浏览器不支持WebSocket");
            }
            // 建立连接
            ws.onopen = function (event) {
                console.log('与服务器建立连接');
                ws.send("您的好友[" + $("#in_name").val() + "]上线了");
            }
            // 接收服务端返回给前端的消息
            ws.onmessage = function (event) {
                $("#chat_content").append(event.data + "\n");
            }

            // 连接关闭
            ws.onclose = function () {
                console.log("与服务器断开连接")
                $("#chat_content").append("用户[" + $("#in_name").val() + "]离开聊天室" + "\n");
                $("#in_name").val("");
            }
        });

        //发送消息
        $("#btn_send").click(function () {
            if (ws == null) {
                alert("该用户不在线");
                return;
            }
            var msg = $("#in_content").val();
            ws.send("用户[" + $("#in_name").val() + "]:" + msg);

        })
        // 离开聊天室
        $("#btn_quit").click(function () {
            ws.send("用户[" + $("#in_name").val() + "]离开聊天室!");
            $("#in_content").val("");
            ws.close();
        })
    })
</script>
</body>
</html>

5.控制器编写

主要编写一个跳转到聊天室的请求

package com.example.websocketchatdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author qx
 * @date 2023/8/22
 * @des 测试
 */
@Controller
@RequestMapping("/chat")
public class IndexController {

    /**
     * 跳转到聊天室
     */
    @GetMapping("/index")
    public String toChat() {
        return "index";
    }
}

6.测试

我们启动项目,打开两个聊天页面。

方便设置两个用户加入聊天室

 

 

然后在一个用户中发送消息,我们可以看到两个聊天窗口的消息同步了。

 

 

当一个用户退出聊天室时,会提示用户退出聊天室。

 这个时候另一个用户发送消息只能自己看到了。

 

 如果想测试多个用户,再从新打开一个页面,进入聊天室就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/85141.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue2入门学习汇总

1.介绍及安装 1.1 介绍 Vue是一套构建用户界面的渐进式框架。Vue只关注视图层&#xff0c;采用自底向上增量开发的设计。Vue的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 学习vue之前主要掌握的知识&#xff1a;HTML、CSS、JavaScript、TypeScript …

基于PyQt+mysql图书管理系统

1 需求分析 针对图书馆的图书管理系统数据库设计&#xff0c;分别对图书馆的读者、一般工作人员和部门负责人进行详细地分析&#xff0c;总结出如下的需求信息: &#xff08;1&#xff09;图书馆中的图书具有书号、书名、作者、馆藏册数、在馆册数、价格、出版社及摘要等必要信…

Java数据库连接池原理及spring boot使用数据库连接池(HikariCP、Druid)

和线程池类似&#xff0c;数据库连接池的作用是建立一些和数据库的连接供需要连接数据库的业务使用&#xff0c;避免了每次和数据库建立、销毁连接的性能消耗&#xff0c;通过设置连接池参数可以防止建立连接过多导致服务宕机等&#xff0c;以下介绍Java中主要使用的几种数据库…

关于数据中心存储智能运维的思考

随着互联网和大数据的快速发展&#xff0c;数据中心存储的重要性也日益凸显。在本文中&#xff0c;将深入探讨数据中心存储智能运维的历史变迁、当前的发展状态和未来的运维趋势。 数据中心存储运维的历史变迁可以分为以下几个阶段&#xff1a; 人工运维阶段 最初&#xff0c…

什么是PPS和TOD时序?授时防护设备是什么?

介绍 PPS和TOD PPS和TOD是两种用于精确时间同步的技术&#xff0c;它们在许多领域都有广泛的应用&#xff0c;总的来说&#xff0c;PPS和TOD被广泛应用于各种需要高度精确时间同步的领域&#xff0c;包括通信、测量、测试、系统集成和计算机网络等。 一、PPS PPS&#xff08…

EasyImage简单图床 - 快速搭建私人图床云盘同时远程访问【无公网IP内网穿透】

憧憬blog主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实战经验&#xff0…

计算机视觉掩模区域与二值图像

掩模区域 在图像处理中&#xff0c;我们经常需要对图像中的某些特定区域进行操作&#xff0c;例如对某个区域进行滤波、变换、裁剪或者其他处理。为了实现这些操作&#xff0c;我们需要明确指定这些区域&#xff0c;这就是掩模区域的作用。 掩模区域通常由一个二值图像表示&…

【使用Node.js搭建自己的HTTP服务器】

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

BGP知识点整理

BGP介绍 边界网关协议BGP&#xff08;Border Gateway Protocol&#xff09;是一种实现自治系统AS&#xff08;Autonomous System&#xff09;之间的路由可达&#xff0c;并选择最佳路由的路径矢量路由协议。目前在IPV4环境下主要使用BGPV4&#xff0c;目前市场上也存在BGPV4&am…

【云原生】3分钟快速在Kubernetes1.25部署Prometheus2.42+Grafana9.5.1+Alertmanager0.25

文章目录 1、简介2、GitHub地址3、环境信息4、安装5、访问Grafana1、简介 Prometheus-operator帮助我们快速创建Prometheus+Grafana+Alertmanager等服务,而kube-prometheus更加完整的帮助我们搭建全套监控体系,这包括部署多个 Prometheus 和 Alertmanager 实例, 指标导出器…

分类预测 | MATLAB实现BWO-TCN-Attention数据分类预测

分类预测 | MATLAB实现BWO-TCN-Attention数据分类预测 目录 分类预测 | MATLAB实现BWO-TCN-Attention数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.BWO-TCN-Attention数据分类预测程序&#xff1b; 2.无Attention适用于MATLAB 2022b版及以上版本&#xf…

【CHI】(十三)链路层

本章介绍了链路层&#xff0c;链路层为节点之间的基于数据包的通信和跨链路的互连提供了一种简化的机制。它包含以下几部分&#xff1a; IntroductionLinkFlitChannelPortNode interface definitionsIncreasing inter-port bandwidthChannel interface signalsFlit packet defi…

【0基础入门Python Web笔记】二、python 之逻辑运算和制流程语句

二、python 之逻辑运算和制流程语句 逻辑运算控制流程语句条件语句&#xff08;if语句&#xff09;循环结构&#xff08;for循环、while循环&#xff09;continue、break和pass关键字控制流程语句的嵌套以及elif 更多实战项目可进入下方官网 逻辑运算 Python提供基本的逻辑运算…

JavaScript基础(Dom操作)

目录 一&#xff0c;BOM模型1.1&#xff0c;BOM可实现功能 二&#xff0c;Window对象的常用属性2.1&#xff0c;Window对象的常用方法2.1-1&#xff0c;open()和close()方法 三&#xff0c;History对象四&#xff0c;Location对象五&#xff0c;Document对象的常用方法六&#…

el-table 实现动态表头 静态内容 根据数据显示动态输入框

直接放代码了 <el-table:data"form.tableDataA"borderstripestyle"width: 100%; margin-top: 20px"><el-table-columnv-for"(category, categoryIndex) in form.tableDataA":key"categoryIndex":label"category.name&qu…

软考高级架构师下篇-12层次式架构设计理论与实践

目录 1. 考情分析2. 层次式体系结构概述3. 表现层框架设计4. 中间层框架设计5. 数据访问层设计6. 数据架构规划与设计7. 物联网层次架构设计8. 前文回顾1. 考情分析 根据考试大纲,层次式架构设计理论与实践知识点会涉及单选题型(约占2~5分)和案例题(25分),本小时内容偏重于方…

Postman接口自动化测试实例

一.实例背景 在实际业务中&#xff0c;经常会出现让用户输入用户密码进行验证的场景。而为了安全&#xff0c;一般都会先请求后台服务器获取一个随机数做为盐值&#xff0c;然后将盐值和用户输入的密码通过前端的加密算法生成加密后串传给后台服务器&#xff0c;后台服务器接到…

【MySQL系列】ALTER语句详解,以及UPDATE,DELECT,TRUNCATE语句的使用+区别

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

PHP数组

数组是一个能在单个变量中存储多个值的特殊变量。 数组的英文是array,可以存入多个不同类型的数据&#xff0c;是一个复合数据类型。 在 PHP 中&#xff0c;有三种类型的数组&#xff1a; 索引数组 - 带有数字 ID 键的数组关联数组 - 带有指定的键的数组&#xff0c;每个键关…

ubuntu18.04安装远程控制软件ToDest方法,针对官网指令报错情况

有时我们在家办公&#xff0c;需要控制实验室的笔记本&#xff0c;因此好用的远程控制软件会让我们的工作事半功倍&#xff01; 常用的远程控制软件有ToDesk&#xff0c;向日葵&#xff0c;以及TeamViewer&#xff0c;但是为感觉ToDesk更流畅一些&#xff0c;所以这里介绍一下…