SpringBoot快速搭建WebSocket并测试

news2024/11/15 23:46:09

目录

    • 简介
    • Pom文件
    • 服务端Service代码
    • 服务端配置代码
    • 服务端Controller:发送消息给客户端
    • 测试
      • 开启ws服务端

简介

WebSocket协议通过在客户端和服务端之间提供全双工通信来进行Web和服务器的交互功能。
在ws应用程序中,服务器发布websocket端点。
一个ws连接的建立,需要客户端和服务端维护一个Key来作为该连接的链接凭证。
客户端向服务端发送wsKey,服务器根据wsKey生成wsAccept返回给客户端,客户端进行相同操作,如果最后的值和服务器返回的Accept的值相匹配,表示握手成功。握手后客户端与服务端就互相发送消息
ws支持文本消息(UTF-8编码)、二进制消息
ws的URI形式:
ws://host:port/path?query
wss://host:port/path?query
ws:未加密连接,默认端口80
wss:加密连接,默认端口443
path:表示服务器内端点的位置
query:路径参数信息

话不多说,上代码

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.9-SNAPSHOT</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>websocketdemo</name>
  <description>Demo project for WebSocket</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.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>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.28</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <excludes>
            <exclude>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
            </exclude>
          </excludes>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
    <repository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <releases>
        <enabled>false</enabled>
      </releases>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>spring-milestones</id>
      <name>Spring Milestones</name>
      <url>https://repo.spring.io/milestone</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>spring-snapshots</id>
      <name>Spring Snapshots</name>
      <url>https://repo.spring.io/snapshot</url>
      <releases>
        <enabled>false</enabled>
      </releases>
    </pluginRepository>
  </pluginRepositories>

</project>

服务端Service代码

这里的代码是websocket的服务端代码,这里相当于是服务器,负责运行wsServer,等待会话,或者发送消息。

package com.example.demo.service;

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.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
* @Description websocket的操作类
* @Author 
* @Date 2023/1/30 10:09
* @Version 1.0
*/
@ServerEndpoint("/websocket/{userId}")
    @Component
    @Slf4j
    public class WebSocketServer {
        /**
* ServerEndpoint注解
* 主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端

**/
        // 与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;

        // session集合,存放对应的session
        private static ConcurrentHashMap<Integer, Session> sessionPool = new ConcurrentHashMap<>();

        // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
        private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();

        /**
* 建立WebSocket连接
*
* @param session
* @param userId 用户ID
*/
        @OnOpen
        public void onOpen(Session session, @PathParam(value = "userId") Integer userId) {
            log.info("WebSocket建立连接中,连接用户ID:{}", userId);
            try {
                Session historySession = sessionPool.get(userId);
                // historySession不为空,说明已经有人登陆账号,应该删除登陆的WebSocket对象
                if (historySession != null) {
                    webSocketSet.remove(historySession);
                    historySession.close();
                }
            } catch (IOException e) {
                log.error("重复登录异常,错误信息:" + e.getMessage(), e);
            }
            // 建立连接
            this.session = session;
            webSocketSet.add(this);
            sessionPool.put(userId, session);
            log.info("建立连接完成,当前在线人数为:{}", webSocketSet.size());
        }
        /**
* 发生错误
*
* @param throwable e
*/
        @OnError
        public void onError(Throwable throwable) {
            throwable.printStackTrace();
        }

        /**
* 连接关闭
*/
        @OnClose
        public void onClose() {
            webSocketSet.remove(this);
            log.info("连接断开,当前在线人数为:{}", webSocketSet.size());
        }

        /**
* 接收客户端消息
*
* @param message 接收的消息
*/
        @OnMessage
        public void onMessage(String message) {
            log.info("收到客户端发来的消息:{}", message);
        }

        /**
* 推送消息到指定用户
*
* @param userId  用户ID
* @param message 发送的消息
*/
        public static void sendMessageByUser(Integer userId, String message) {
            log.info("用户ID:" + userId + ",推送内容:" + message);
            Session session = sessionPool.get(userId);
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("推送消息到指定用户发生错误:" + e.getMessage(), e);
            }
        }

        /**
* 群发消息
*
* @param message 发送的消息
*/
        public static void sendAllMessage(String message) {
            log.info("发送消息:{}", message);
            for (WebSocketServer webSocket : webSocketSet) {
                try {
                webSocket.session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                log.error("群发消息发生错误:" + e.getMessage(), e);
                }
                }
                }
                }

服务端配置代码

这里是进行config的配置项
首先是对application进行配置

server:
  port: 8080
websocket:
  port: 6001
  action: ws://127.0.0.1
  thread:
    boss: 12
    work: 12

然后新建一个配置类

package com.example.demo.config;

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

import javax.servlet.ServletContext;
import javax.servlet.ServletException;

/**
* @Description websocket的配置文件, 开启websocket支持
* @Author  
* @Date 2023/1/30 10:03
* @Version 1.0
*/
@Configuration
    public class WebSocketConfig  implements ServletContextInitializer {
        /**
* 扫描所有带有@ServerEndpoint的注解成为websocket,
* 如果使用外置的tomcat就不需要该配置文件
*/
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {

        }
    }

服务端Controller:发送消息给客户端

这里的代码就是测试发信息的,你可以通过controller进行推送消息给当前链接的ws客户端

package com.example.demo.controller;

import com.example.demo.service.WebSocketServer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @Description 服务端向客户端发送消息
 * @Author  
 * @Date 2023/1/30 13:39
 * @Version 1.0
 */
@RestController
public class WsController {
    //给指定用户发消息
    @GetMapping("/sendone/{message}/{userId}")
    public String sendmessage(@PathVariable("message") String message, @PathVariable("userId") Integer userId) throws IOException {
        WebSocketServer.sendMessageByUser(userId,message);
        return "ok";
    }

    //这个可以后台群发,所有用户都能看到
    @GetMapping("/sendall/{message}")
    public String sendmessageall(@PathVariable("message") String message) throws IOException {
        WebSocketServer.sendAllMessage(message);
        return "ok";
    }
}

测试

一般来说,测试需要js的前端代码实现,这里为了简化,我们直接调用网页形式进行测试

开启ws服务端

  1. 把上述所有的springboot项目启动,主要就是把WebSocketServer启动起来
  2. 打开链接http://www.jsons.cn/websocket/
    在这里插入图片描述
    输入你的ws地址,1是你的客户端ID,这个是自定义的,这个网页模仿的就是客户端。
    在这里插入图片描述
    /websocket 这个则是按照ws服务端代码中的规定写路由的
  3. 点击链接
    正常你会看到
    在这里插入图片描述
    然后就可以愉快的客户端向服务端发信息了。
    服务端向客户端发信息也很简单,之前写的那个controller就是干这个的,不再演示。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通用`Query`解决方案

文章目录通用Query解决方案简介什么是QueryQuery类别Query基本使用SQL Query基本使用自定义Query基本使用现状方案通过Json数据或方法动态生成Query 通过Select Sql语句动态生成Query通过Query生成动态Query支持传统的Query并通过参数形式生成Query列定义通用Query&#xff0c;…

nacos区分权限

背景 nacos的默认是不进行分配权限的&#xff0c;那么这样就带来了一个问题&#xff0c;如果多项目共同使用一个nacos&#xff0c;可以带了一个情况是开发人员误操作&#xff0c;把其他项目的nacos配置文件更改或者删除。那么如何解决这个问题呢&#xff1f;就是把nacos进行分…

TF-A源码移植

1.对tf-a源码进行解压$> tar xfz tf-a-stm32mp-2.2.r2-r0.tar.gz 2.打补丁进入/home/ubuntu/FSMP1A/tf-a-stm32mp-2.2.r2-r0/tf-a-stm32mp-2.2.r2-r0/tf-a-stm32mp-2.2.r2目录执行for p in ls -1 ../../*.patch; do patch -p1 < $p; done3.配置工具链1)进入/home/ubuntu/…

GJB 5000B二级-ESM外部供方管理

一、主要变化情况 新增3项(红色)、删除1项(黄色)、合并2项(绿色)、修订4项(蓝色) 将原标准过程域名称“供方协议管理”改为“外部供方管理”,其适应范围包括所以形式的外部提供过程、产品和服务协议,同时增加对外部供方按协议提供过程、产品和服务能力评价的内容,…

小偷和抢劫是被怎么遏制的?

小偷和抢劫是怎么被消灭的&#xff1f; 是被摄像头消灭的&#xff01; 一切土地和实物都会被安装传感设备监控 农业生产用地也会被物联检测 趣讲大白话&#xff1a;万物互联时代稳步实现 *********** 全国有5000万亩茶园 20年内一定会被物联网化 进入精耕农业时代 20年后&…

一篇文章带你了解自动化测试开发

都讲自动化测试开发&#xff0c;当然要把开发自动化测试框架也当做一个项目来做。这时候&#xff0c;就需要考虑应该选择何种类型的自动化测试框架&#xff1a;数据驱动、关键字驱动、还是Junit ,TestNG ? 抑或直接利用现有的开源自动化测试框架&#xff0c;如Robot Framework…

Mysql内核查询成本计算实战(一)

目录 Mysql内核查询成本计算实战&#xff08;一&#xff09; Optimizer Trace 什么是成本 I/O成本 CPU成本 单表查询的成本 MySQL查询成本计算实战 1.根据搜索条件&#xff0c;找出所有可能使用的索引 2. 计算全表扫描的代价 3. 计算使用不同索引执行查询的代价 4. 对…

CAD中怎么局部升降桥架?CAD局部升降操作技巧

在使用浩辰CAD电气软件绘制电气图纸的时候&#xff0c;常常会用到三维桥架中的一些功能来进行桥架的CAD设计工作&#xff0c;为了让大家对此有更深入的了解&#xff0c;接下来的CAD设计教程就和小编一起来看看正版CAD软件——浩辰CAD电气软件的三维桥架中局部升降功能的相关使用…

Magisk模块开发指南

BusyBox Magisk整合了功能完整的BusyBox二进制文件(包括对SELinux的完整支持)。执行文件位于/data/adb/magisk/busybox。Magisk的BusyBox支持运行时可切换的“ASH Standalone Shell Mode(ASH独立Shell模式)”。这种独立模式的意思是,在ashshell的中的BusyBox运行时,无论PATH…

MySQL核心参数优化文件my.ini详解

一.数据库服务器配置 CPU&#xff1a;48C 内存&#xff1a;128G DISK&#xff1a;3.2TSSD 二.CPU的优化 innodb_thread_concurrency32 表示SQL经过解析后&#xff0c;允许同时有32个线程去innodb引擎取数据&#xff0c;如果超过32个&#xff0c;则需要排队&#xff1b; 值太…

spring系列 SpringMVC-拦截器

拦截器&#xff08;Interceptor&#xff09;是在SpringMVC中动态拦截控制器方法的执行。 拦截器执行流程&#xff1a; 拦截器与过滤器区别 归属不同&#xff1a;Filter属于Servlet技术&#xff0c;Interceptor属于SpringMVC技术 拦截内容不同&#xff1a;Filter对所有访问进…

【Mysql第二期 MySQL环境搭建】

文章目录01.为什么要安装新版本&#xff1f;02.官网下载mysql03.安装配置初始化mysql04.查看 MySQL服务05.验证是否安装成功06.修改root密码07.如果有navicat工具可以在测试一下&#xff1a;01.为什么要安装新版本&#xff1f; mysql8.x版本和msyql5.x版本zip安装的方式大同小…

证券交易金融知识学习(1)

学习目标&#xff1a; 需要做一些关于投资交易软件的测试&#xff0c;需要了解操作背后的交易意义&#xff0c;需要学习一些金融基础知识。本人是金融证券交易的小白&#xff0c;从0开始学习。故记录一些金融知识学习的笔记&#xff0c;比较零散&#xff0c;目的是为了让自己复…

Spring-DI相关内容

Spring-DI相关内容 5&#xff0c;DI相关内容 前面我们已经完成了bean相关操作的讲解&#xff0c;接下来就进入第二个大的模块DI依赖注入&#xff0c;首先来介绍下Spring中有哪些注入方式? 我们先来思考 向一个类中传递数据的方式有几种? 普通方法(set方法)构造方法 依赖注…

【数据结构】8.4 选择排序

文章目录1. 简单选择排序简单选择排序算法简单排序算法分析2. 堆排序堆的定义堆的调整堆的建立堆排序算法堆排序算法分析1. 简单选择排序 基本思想 在待排序的数据中选出最大&#xff08;小&#xff09;的元素放在其最终的位置。 基本操作 首先通过 n - 1 次关键字比较&…

计算机SCI期刊能一稿多投吗? - 易智编译EaseEditing

首先建议不要一稿多投&#xff0c;投稿前要对目标期刊了解清楚&#xff0c;是什么方向&#xff0c;什么水平的。 可以看看期刊近期发表的文章&#xff0c;是什么方向的&#xff0c;这样会更精准。 一稿多投就是广撒网嘛&#xff0c;还不如做好功课&#xff0c;找到对应期刊&a…

网络知识详解之:HTTPS通信原理剖析(对称、非对称加密、数字签名、数字证书)

网络知识详解之&#xff1a;HTTPS通信原理剖析&#xff08;对称、非对称加密、数字签名、数字证书&#xff09; 计算机网络相关知识体系详解 网络知识详解之&#xff1a;TCP连接原理详解网络知识详解之&#xff1a;HTTP协议基础网络知识详解之&#xff1a;HTTPS通信原理剖析&…

Es6 扩展运算符... ,以及rest和arguments

扩展运算符... … 扩展运算符能将 数组 转换为逗号分隔的 参数序列 应用场景&#xff1a; 多个数组的合并 var arr4 [1, 2, 3];var arr5 [4, 5, 6];var arr6 [...arr4, ...arr5];//合并数组 也可以为数组的一部分arr6的值应为[1,2,3,4,5,6] 数组的克隆&#xff0…

C语言--指针与数组

目录指针运算&#xff08;补&#xff09;指针指针指针的关系运算&#xff08;补&#xff09;指针与数组数组名二级指针指针数组指针运算&#xff08;补&#xff09; 指针指针 上一篇博客我们介绍了指针运算中的三种常见运算&#xff1a;指针整数&#xff0c;指针关系运算&…

关于android studio安装篇

前言&#xff1a;本文安装环境为windows系统&#xff0c;调试环境AVD&#xff08;电脑上运行的“虚拟手机”&#xff09;&#xff0c;安装android studio之前需安装jdk&#xff0c;配置好jdk的环境变量。解释&#xff1a;android运行调试环境有三种方式&#xff0c;真机、AVD&a…