SpringBoot+WebSocket实现即时通讯的方法详解

目录
  • 环境信息
  • 服务端实现
    • 导入依赖
    • 创建配置类
    • 创建一个注解式的端点并在其中通过配套注解声明回调方法
    • 服务端主动发送消息给客户端
  • 客户端实现
    • Java客户端实现
    • 在前端环境(vue)中使用websocket

环境信息

名称 版本号
Spring Boot 2.4.5
Idea 2021.3.2

服务端实现

导入依赖

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

注意:Spring Boot在父工程中已经管理了websocket的版本信息,所以不用指定版本号也是可以的

创建配置类

package com.fenzhichuanmei.configuration;

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

/**
 * @author Yi Dai 484201132@qq.com
 * @since 2022/5/13 11:34
 */

@Configuration
public class WebsocketConfiguration {

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

}

创建此配置类的目的只是为了把ServerEndpointExporter 这个类的实例交给spring 容器进行管理,您可以用任意一种方式交给容器,如使用@Import(ServerEndpointExporter.class)这种方式等进行操作;此处只是我的编码风格如此;并非必须这样操作

创建一个注解式的端点并在其中通过配套注解声明回调方法

package com.fenzhichuanmei.websocket;

import com.fenzhichuanmei.websocket.utils.SessionManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

/**
 * @author Yi Dai 484201132@qq.com
 * @since 2022/3/7 15:47
 */

@Slf4j
@Component
@ServerEndpoint("/arcticFoxServerEndpoint/{websocketClientType}")
public class ArcticFoxServerEndpoint {

    private static SessionManager sessionManager;

    @Resource
    public void setProcessor(SessionManager sessionManager) {
        ArcticFoxServerEndpoint.sessionManager = sessionManager;
    }

    /**
     * 建立连接成功的回调方法
     *
     * @param session             会话对象
     * @param websocketClientType 此参数就是路径中{websocketClientType}位置传入的参数
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("websocketClientType") int websocketClientType) {
        sessionManager.onOpen(session, websocketClientType);
    }

    /**
     * 当会话关闭时执行的回调方法
     *
     * @param session             会话对象
     * @param websocketClientType 此参数就是路径中{websocketClientType}位置传入的参数
     */
    @OnClose
    public void onClose(Session session, @PathParam("websocketClientType") int websocketClientType) {
        sessionManager.onClose(session, websocketClientType);
    }

    /**
     * 当收到客户端信息时执行的回调方法
     *
     * @param session             会话对象
     * @param message             客户端传递过来的信息
     * @param websocketClientType 此参数就是路径中{websocketClientType}位置传入的参数
     */
    @OnMessage
    public void onMessage(Session session, String message, @PathParam("websocketClientType") int websocketClientType) {
        sessionManager.onMessage(session, message, websocketClientType);
    }

    /**
     * 当发生错误时的回调方法
     *
     * @param session             会话对象
     * @param e                   异常对象
     * @param websocketClientType 此参数就是路径中{websocketClientType}位置传入的参数
     */
    @OnError
    public void onError(Session session, Throwable e, @PathParam("websocketClientType") int websocketClientType) {
        sessionManager.onError(session, e, websocketClientType);
    }

}

@ServerEndpoint注解标注此类为一个服务端的端点类,此注解有一个必须的参数,用于指定客户端访问的地址,本案例中为:/arcticFoxServerEndpoint,而路径后面的/{websocketClientType}这个是路径中参数的占位符,有点类似与Spring Mvc中Rest接口和@PathVariable注解的作用

注意事项: 一定要将此类交给spring 容器进行管理!!还有一个坑就是,此类的实例时非单例的,所以如果要在此类中注入其他的bean,不能使直接在属性上使用@Resource注解或者@Autowired等注解进行注入,否则会报错。正确操作应该是把要注入的字段设置为静态的,然后通过非静态的set方法进行注入,具体代码请看上方实例

服务端主动发送消息给客户端

通过上面的代码我们可以知道每个回调方法中都会收到一个Session对象,正如您所想,要向客户端发送消息正是要借助此对象;Session对象有一个getAsyncRemote方法,调用此方法可以得到一个RemoteEndpoint.Async对象,查看此对象,发现有很多send打头的方法;

是的,这些方法就是发送消息的方法,博主这个项目中主要是通过JSON来进行交互的,所以我使用了sendText方法,示例代码:

RemoteEndpoint.Async asyncRemote = session.getAsyncRemote();
asyncRemote.sendText(jsonString);

很显然中转变量asyncRemote 没什么太大的用处,不如直接写成:

session.getAsyncRemote().sendText(jsonString);

通过方法名看到,似乎还可以发送对象,二进制序列等,博主没有深入研究,有兴趣的小伙伴可以尝试尝试

客户端实现

一般来讲客户端应该是用Java Script实现,但是博主这个项目比较特殊,需要用Java来实现客户端,下面博主先以Java客户端说明其实现细节,然后再说再前端如何实现

Java客户端实现

导入依赖

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.5.3</version>
</dependency>

其实Java中实现WebSocket的第三方包还有很多,博主这个地方使用的是Java-WebSocket,有兴趣的小伙伴可以试试其他的包

建立连接和处理回调

package com.fenzhichuanmei.websocket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fenzhichuanmei.components.PaymentComponent;
import com.fenzhichuanmei.pojo.Instructions;
import com.fenzhichuanmei.utils.WebsocketClientType;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author Yi Dai 484201132@qq.com
 * @since 2022/5/13 10:16
 */

@Slf4j
@Component
public class ArcticFoxWebSocketClient {

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private PaymentComponent paymentComponent;

    @Resource
    private ArcticFoxWebSocketClientProperties properties;

    public void establishConnection() throws URISyntaxException {
        WebSocketClient webSocketClient = new WebSocketClient(new URI(String.format("%s/%d", properties.getWebSocketServerUrl(), WebsocketClientType.PAYMENT_DEVICE))) {
            @Override
            public void onOpen(ServerHandshake serverHandshake) {
                log.info("WebSocketClient: onOpen : {}", serverHandshake);
            }

            @Override
            public void onMessage(String jsonString) {
                try {
                    Instructions instructions = objectMapper.readValue(jsonString, Instructions.class);
                    if (instructions.getType() == Instructions.NOTICE_PAYMENT) {
                        paymentComponent.queryAnUnpaidOrdersAndPay();
                    } else {
                        throw new RuntimeException("错误的指令类型");
                    }
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onClose(int i, String s, boolean b) {
                log.info("WebSocketClient: onClose : i:{},s:{},b:{}", i, s, b);
                try {
                    Thread.sleep(1000 * 20);
                    establishConnection();
                } catch (InterruptedException | URISyntaxException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(Exception e) {
                log.error("WebSocketClient: onError {}", e.getMessage());
            }
        };
        webSocketClient.connect();
        while (!(webSocketClient.getReadyState() == ReadyState.OPEN)) {
            try {
                Thread.sleep(1000 * 2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.info("WebSocketClient: connection established successfully");
    }

    @Data
    @Component
    @ConfigurationProperties("arctic-fox-web-socket-client.properties")
    public static class ArcticFoxWebSocketClientProperties {

        private String webSocketServerUrl;

    }
}

代码解释: 其实我的establishConnection方法中上来就实例化了一个WebSocketClient 类的实例,请注意,此类是个抽象类,我在这里用匿名实现类的方式实现的,此类有几个抽象方法需要实现,也就是onOpen,onMessage,onClose,onError四个方法,其作用其实已经是很见名知意了,和服务端的回调方法一样,就不过多解释;实例化此类需要传入一个URI对象,这个URI对象其实就是封装了对服务端连接的地址,由于博主不希望把服务端的地址给写死了,所以我配置到了配置文件中,然后通过String.format静态方法配合占位符拼接url地址和参数;路径的规则是:协议名://IP地址(或域名):端口号/服务端声明的地址/参数;举个例子:

ws://192.168.88.88:8080/arcticFoxServerEndpoint/1

ws://localhost:8080/arcticFoxServerEndpoint/2

ws://为协议;实例化WebSocketClient 类的实例之后,调用其connect()方法即开始建立连接,调用getReadyState()方法可以获得其状态;由于我的服务端可能随时都连不上,所以我在客户端的onClose回调函数中进行了一个递归(20秒后),用于重新连接。

客户端向服务端发送消息

通过WebSocketClient 类的实例,我们可以看到有以下方法,很明显send方法就是用来发送消息使用的

示例代码:

//判断一下是否为空
if (Objects.nonNull(webSocketClient)) {
    try {
    	//通过jackson将对象转换为json字符串(非必须)
        String jsonString = objectMapper.writeValueAsString(feedback);
        //发送信息
        webSocketClient.send(jsonString);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
} else {
    log.warn("no connection established");
}

在前端环境(vue)中使用websocket

安装reconnecting-websocket包(非必须)

npm i --save reconnecting-websocket

安装这个包是为了websocket能在断线之后重新连接,其实不使用这个包也是可以用原生Java Script实现的;但是他和原生的api几乎一样;

示例代码:

import ReconnectingWebSocket from "reconnecting-websocket";

export default function initializationWebsocket() {
    let reconnectingWebSocket = new ReconnectingWebSocket(`ws://localhost:8080/arcticFoxServerEndpoint/${2}`);
    reconnectingWebSocket.onopen = event => {
        console.log("on open :", event);
    };

    reconnectingWebSocket.onmessage = event => {
    	//event对象中data存储的就是服务端发送过来的消息
        let parse = JSON.parse(event.data);
        console.log("webSocket on message :", parse);
    };

    reconnectingWebSocket.onclose = event => {
        console.log(event);
    };

    reconnectingWebSocket.onerror = event => {
        console.log(event);
    };

	//窗口关闭时断开连接
    window.onbeforeunload = function () {
        reconnectingWebSocket.close();
    }
}

在前端中实现websocket就比较简单了,就上面的几行代码即可,不用调用其他函数进行连接,实例化之后就开始连接了

想服务端发送信息

在前端中发送信息就更简单了,直接调用reconnectingWebSocketsend方法,传入要发送的数据即可

到此这篇关于SpringBoot+WebSocket实现即时通讯的方法详解的文章就介绍到这了,更多相关SpringBoot WebSocket即时通讯内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot整合websocket实现即时通信聊天

    目录 一.技术介绍 1.1 客户端WebSocket 1.1.1 函数 1.1.2 事件 1.2 服务端WebSocket 二.实战 2.1.服务端 2.1.1引入maven依赖 2.1.2 编写配置类 2.1.3 编写WebSocketService服务类 2.1.4 建立连接 2.1.5 关闭连接 2.1.6 发送消息 2.1.7 监听错误 2.2 客户端 2.2.1 主页面 2.2.1 聊天页面 三.开源地址 四.参考文献 一.技术介绍 线上演示地址:http://chat.breez.w

  • 使用springboot整合websocket实现群聊教程

    目录 先上效果图: 先来准备工作导入依赖 导入依赖后扫描启用 接收前端传回数据 其中重点就是4个注解 **@OnOpen,@OnClose,@OnMessage,@OnError** 前端页面代码 模板引擎代码如下 最后效果图如下 先上效果图: 相对来说更好看那么一点但是,实现代码都是一样的. 先来准备工作导入依赖 <!--websocket依赖--> <dependency> <groupId>org.springframework.boot</groupId&

  • SpringBoot实现WebSocket即时通讯的示例代码

    目录 1.引入依赖 2.WebSocketConfig 开启WebSocket 3.WebSocketServer 4.测试连接发送和接收消息 5.在线测试地址 6.测试截图 1.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>

  • SpringBoot中webSocket实现即时聊天

    即时聊天 这个使用了websocket,在springboot下使用很简单.前端是小程序,这个就比较坑,小程序即时聊天上线需要域名并且使用wss协议,就是ws+ssl更加安全.但是要上线这还不够,你必须为企业主体开发者.个人开发者即时聊天属于社交.不在服务类目内,审核会不通过!!! 功能 :我们的小程序是个二手交易小程序,即时聊天对于一个后台服务器只是单核2g的来说有点抗不住.所以在双方都在线的时候没有存储聊天消息,只是在单方不在线时存储了离线消息.而且只能发三条离线消息.仿照了csdn的聊天.

  • SpringBoot+WebSocket实现多人在线聊天案例实例

    目录 1.pom.xml 2.消息实体类 3.controller 4.WebSocket的配置文件 5.前端发送消息页面 6.测试 6.1.客户端A 6.2.客户端B 1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.o

  • SpringBoot+WebSocket实现即时通讯的方法详解

    目录 环境信息 服务端实现 导入依赖 创建配置类 创建一个注解式的端点并在其中通过配套注解声明回调方法 服务端主动发送消息给客户端 客户端实现 Java客户端实现 在前端环境(vue)中使用websocket 环境信息 名称 版本号 Spring Boot 2.4.5 Idea 2021.3.2 服务端实现 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp

  • SpringBoot注入配置文件的3种方法详解

    这篇文章主要介绍了SpringBoot注入配置文件的3种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 方案1:@ConfigurationProperties+@Component 定义spring的一个实体bean装载配置文件信息,其它要使用配置信息是注入该实体bean /** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配

  • 在Android中使用WebSocket实现消息通信的方法详解

    前言 消息推送功能可以说移动APP不可缺少的功能之一,一般简单的推送我们可以使用第三方推送的SDK,比如极光推送.信鸽推送等,但是对于消息聊天这种及时性有要求的或者三方推送不满足业务需求的,我们就需要使用WebSocket实现消息推送功能. 基本流程 WebSocket是什么,这里就不做介绍了,我们这里使用的开源框架是https://github.com/TakahikoKawasaki/nv-websocket-client 基于开源协议我们封装实现WebSocket的连接.注册.心跳.消息分

  • SpringBoot向容器注册bean的方法详解

    目录 简介 法1:@Component 法2:@Configuration+@Bean 法3:@Import等 法4:FactoryBean 简介 本文用示例介绍SpringBoot如何向容器注册bean(即:将对象加入容器). 法1:@Component (@Controller/@Service/@Repository也可以,因为它里边包含@Component) 默认是加载和Application类所在同一个目录下的所有类,包括所有子目录下的类. 当启动类和@Component分开时,如果启

  • SpringBoot+Elasticsearch实现数据搜索的方法详解

    目录 一.简介 二.代码实践 2.1.导入依赖 2.2.配置环境变量 2.3.创建 elasticsearch 的 config 类 2.4.索引管理 2.5.文档管理 三.小结 一.简介 在上篇 ElasticSearch 文章中,我们详细的介绍了 ElasticSearch 的各种 api 使用. 实际的项目开发过程中,我们通常基于某些主流框架平台进行技术开发,比如 SpringBoot,今天我们就以 SpringBoot 整合 ElasticSearch 为例,给大家详细的介绍 Elast

  • SpringBoot结合Redis实现序列化的方法详解

    目录 前言 配置类 配置 Jackson2JsonRedisSerializer 序列化策略 配置  RedisTemplate 配置缓存策略 测试代码 完整代码 前言 最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题.这里分享一下我最新学到的写法 默认情况下,Spring 为我们提供了一个 RedisTemplate 来进行对 Redis 的操作,但是 RedisTemplate 默认配置的是使用Java本机序列化.

  • SpringBoot实现登录拦截器的方法详解

    在项目目录下建立两个包:inter 与contsfig 在inter新建层中实现HandlerInterceptor的继承类 package com.example.gameboxadminserver.inter; import com.example.gameboxadminserver.entity.User; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.

  • SpringBoot注入自定义的配置文件的方法详解

    目录 一.简介 二.代码实践 2.1 通过@value注解实现参数加载 2.2 通过@ConfigurationProperties注解实现参数加载 2.3 通过@PropertySource注解实现配置文件加载 2.4 通过自定义环境处理类,实现配置文件的加载 2.5 最后,我们来介绍一下yml文件读取 一.简介 在实际的项目开发过程中,我们经常需要将某些变量从代码里面抽离出来,放在配置文件里面,以便更加统一.灵活的管理服务配置信息.比如,数据库.eureka.zookeeper.redis.

  • SpringBoot使用freemarker导出word文件方法详解

    目录 1.前言 2.需求说明 3.编码 3.1.导入依赖 3.2.接口编写 3.3.工具类 3.4.ftl文件 3.5.测试 4.word转pdf 5.总结 1.前言 在项目中我们有时间需要根据一个word模板文档,批量生成其他的word文档,里面的有些值改变一下而已,那怎么做呢? 2.需求说明 假如说,现在我有个模板文档,内容如下: 现在上面文档里面有如下变量: username:员工姓名 idno:身份证号码 hireDate:入职日期 work:职位 endDate:离职日期 现在我需要针

  • SpringBoot实现项目文件上传的方法详解

    目录 一.首先抛出问题 二.解决思路 三.直接看源码就懂了,下面是controller 四.结尾 一.首先抛出问题 以阿里云oss文件上传为例,假设我的需求是这样的,我需要发布一条动态,这条动态呢可以是图片.语音.视频,3种类型,每种类型的上传我必须要限制它的文件大小,超过了我就不能让他上传的.如果传统的方式,那就是创建3个上传类型bucket对应图片.语音和视频,其实这种做法是可以的,但是怎么说呢,还不够优雅,如果当这个动态有越来越多种类型,你是不是要建立N个类型对应呢,所以就会使得bucke

随机推荐