Springboot使用websocket服务端+客户端(断线重连)


前言

提示:这里可以添加本文要记录的大概内容:

SpringBoot中使用Websocket


提示:以下是本篇文章正文内容,下面案例可供参考

一、先创建好SpringBoot框架

二、使用步骤

1.使用maven引入依赖

代码如下:

<!--webSocket-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
 <dependency>
 <groupId>org.apache.commons</groupId>
     <artifactId>commons-lang3</artifactId>
     <version>3.8.1</version>
 </dependency>

2.创建服务端

创建WebSocketServer

package com.panbl.websocketdemo.websocket;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * websocket的处理类。
 * 作用相当于HTTP请求
 * 中的controller
 */
@Component
@Slf4j
@ServerEndpoint("/ws/{userId}")
public class WebSocketServer {

    /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<String, List<WebSocketServer>> devWebSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId = "";

    /**
     * 连接建立成
     * 功调用的方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) {
        this.session = session;
        //userId = session.getId()+"_"+userId; //拼接
        this.userId=userId;
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //加入set中
            webSocketMap.put(userId,this);
        }else{
            //加入set中
            webSocketMap.put(userId,this);
            //在线数加1
            addOnlineCount();
        }
        log.info("用户连接:"+userId+",当前在线用户为:" + getOnlineCount());
        sendMessage("{\"status\":0,\"msg\":\"连接成功\"}");
    }

    /**
     * 连接关闭
     * 调用的方法
     */
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:"+userId+",当前在线用户为:" + getOnlineCount());
    }

    /**
     * 收到客户端消
     * 息后调用的方法
     * @param message
     * 客户端发送过来的消息
     **/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:"+userId+",报文:"+message);
        //可以群发消息
        //消息保存到数据库、redis
        if(StringUtils.isNotBlank(message)){
            try {

            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }


    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {

        log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务
     * 器主动推送
     */
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *发送自定
     *义消息
     **/
    public static void sendInfo(String message, String userId) {
        log.info("发送消息到:"+userId+",报文:"+message);
        if(StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            log.error("用户"+userId+",不在线!");
        }
    }

    /**
     * 获得此时的
     * 在线用户数
     * @return
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 在线人
     * 数加1
     */
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    /**
     * 在线人
     * 数减1
     */
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

}


创建WebSocketConfig

package com.lst.cabinet.config;

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

/**
 * @author Panbanglin
 * @create 2022/4/11
 */
@Configuration
public class WebSocketConfig {

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

以上为服务端代码。


3.创建客户端-web版本

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
    let socket;

    function openSocket() {

        const socketUrl = "ws://localhost:8080/ws/" + $("#userId").val();
        console.log(socketUrl);
        if (socket != null) {
            socket.close();
            socket = null;
        }
        socket = new WebSocket(socketUrl);
        //打开事件
        socket.onopen = function () {
            console.log("websocket已打开");
        };
        //获得消息事件
        socket.onmessage = function (msg) {
            console.log(msg.data);
            //发现消息进入,开始处理前端触发逻辑
        };
        //关闭事件
        socket.onclose = function () {
            console.log("websocket已关闭");
        };
        //发生了错误事件
        socket.onerror = function () {
            console.log("websocket发生了错误");
        }
    }

    function sendMessage() {

        // socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
        // console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
    }
</script>
<body>
<p>【socket开启者的ID信息】:
<div><input id="userId" name="userId" type="text" value="3000000842"></div>
<p>【客户端向服务器发送的内容】:
<div><input id="toUserId" name="toUserId" type="text" value="20">
    <input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【操作】:
<div><a style="color: red" onclick="openSocket()">开启socket</a></div>
<p>【操作】:
<div><a onclick="sendMessage()">发送消息</a></div>
</body>

</html>

</html>

web版连接演示

在这里插入图片描述
web页面客户端连接演示
后端服务器演示

4.SpringBoot作为客户端 带断线重连

新建SpringBoot项目

<!--websocket作为客户端-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>
<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.5.2</version>
</dependency>

1.创建MyWebSocketClient

package com.panbl.springbootwebsocketclient.websocket;

import com.panbl.springbootwebsocketclient.tool.ByteUtils;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Map;

/**
 * @author PanBangLin
 * @version 1.0
 * @date 2022/4/19 22:38
 */
@Slf4j
public class MyWebSocketClient extends org.java_websocket.client.WebSocketClient {



    public MyWebSocketClient(URI serverUri) {
        super(serverUri);
    }

    public MyWebSocketClient(URI serverUri, Draft protocolDraft) {
        super(serverUri, protocolDraft);
    }

    public MyWebSocketClient(URI serverUri, Draft protocolDraft, Map<String, String> httpHeaders, int connectTimeout) {
        super(serverUri, protocolDraft, httpHeaders, connectTimeout);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        log.info("[websocket] 连接成功");
        //devOrder.subscribeDev("33255773800487108280");
    }

    @Override
    public void onMessage(String message) {
        log.info("[websocket] 收到消息={}", message);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        log.info("[websocket] 收到消息={}", ByteUtils.getString(bytes));

    }

    @Override
    public void onClose(int i, String s, boolean b) {
        log.info("[websocket] 退出连接");
    }

    @Override
    public void onError(Exception e) {
        e.printStackTrace();
        log.info("[websocket] 连接错误={}", e.getMessage());
    }
}

2.新建工具类解析ByteBuffer 数据 ByteUtils

package com.cw.devsocket.Tool;

import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * @author Panbanglin
 * @create 2022/4/19
 */
public class ByteUtils {

    /**
     * String 转换 ByteBuffer
     * @param str
     * @return
     */
    public static ByteBuffer getByteBuffer(String str)
    {
        return ByteBuffer.wrap(str.getBytes());
    }

    /**
     * ByteBuffer 转换 String
     * @param buffer
     * @return
     */
    public static String getString(ByteBuffer buffer)
    {
        Charset charset = null;
        CharsetDecoder decoder = null;
        CharBuffer charBuffer = null;
        try
        {
            charset = Charset.forName("UTF-8");
            decoder = charset.newDecoder();
            // charBuffer = decoder.decode(buffer);//用这个的话,只能输出来一次结果,第二次显示为空
            charBuffer = decoder.decode(buffer.asReadOnlyBuffer());
            return charBuffer.toString();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
            return "";
        }
    }
}

新建WebSocketConfig

package com.panbl.springbootwebsocketclient.config;

import com.panbl.springbootwebsocketclient.websocket.MyWebSocketClient;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

/**
 * @author PanBangLin
 * @version 1.0
 * @date 2022/4/19 22:40
 */
@Component
@Slf4j
public class WebSocketConfig {

    @Autowired
    private Environment env;

    @Bean
    public WebSocketClient webSocketClient() {
        //String ws=env.getProperty("dev.webSocket")+ UUID.randomUUID().toString();
        String ws="ws://localhost:8080/ws/"+ UUID.randomUUID().toString();
        //String ws="ws://127.0.0.1:8181/ws/v1/cabinet/status/"+ UUID.randomUUID().toString();
        try {
            WebSocketClient webSocketClient = new MyWebSocketClient(new URI(ws));
            webSocketClient.connect();
            Timer t = new Timer();
            t.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("进入定时器");
                    if(webSocketClient.isClosed()){
                        log.error("断线重连");
                        webSocketClient.reconnect();
                    }
                }
            },1000,5000);
            return webSocketClient;
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return null;
    }

}

4.演示

断线重连
服务端

总结

源码码云地址:https://gitee.com/panbanglin/spring-boot-websocket-usage.git

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 使用 Spring Boot 实现 WebSocket 服务端和网页客户端需要以下步骤: 1. 在项目的 pom.xml 文件中添加 spring-boot-starter-websocket 依赖。 2. 创建一个 WebSocketConfig 类,并使用 @EnableWebSocketMessageBroker 注解开启 WebSocket 消息代理。 3. 在 WebSocketConfig 类中配置消息代理,可以使用 @Autowired 注入 SimpMessagingTemplate 类。 4. 创建一个 WebSocketController 类,并使用 @Controller 注解标记为控制器。在该类中可以定义处理客户端请求的方法,使用 @MessageMapping 注解标记方法,并使用 SimpMessagingTemplate 向客户端发送消息。 5. 在网页客户端中,使用 JavaScript 和 WebSocket API 连接服务器并发送和接收消息。 6. 在 spring boot 启动类中添加 @EnableWebSocket 即可。 更多细节请参考Spring官网相关文档。 ### 回答2: 在使用Spring Boot实现WebSocket服务端和网页客户端时,需要进行以下步骤: 1. 首先,创建一个Spring Boot项目,并在pom.xml文件中添加相关的依赖项,包括Spring Web和Spring WebSocket依赖。 2. 创建一个WebSocket配置类,通过@EnableWebSocket注解启用WebSocket,并实现WebSocketConfigurer接口,重写registerWebSocketHandlers方法。 3. 在registerWebSocketHandlers方法中,创建一个WebSocketHandler对象,并使用registerHandler方法注册该Handler,并指定相关的WebSocket连接路径。 4. 在WebSocketHandler中,继承TextWebSocketHandler,重写handleTextMessage方法来处理接收到的文本消息。 5. 在handleTextMessage方法中,可以处理接收到的消息,并通过WebSocketSession对象的sendMessage方法发送消息给客户端。 6. 创建一个Web页面作为WebSocket客户端,并使用JavaScript中的WebSocket对象进行连接,指定WebSocket连接路径。 7. 在客户端使用WebSocket对象的onopen、onmessage、onclose和onerror方法来处理连接建立、接收到消息、连接关闭和连接错误的情况。 使用以上步骤,可以实现一个简单的WebSocket服务端和网页客户端。当客户端连接到服务端时,服务端可以接收到客户端发送的消息,并进行相应的处理,然后将处理结果发送给客户端。而客户端可以通过WebSocket对象发送消息给服务端,并接收到服务端发送的消息,完成双向通信的功能。 ### 回答3: 使用Spring Boot实现WebSocket服务端和网页客户端可以通过以下几个步骤完成。 1. 首先,在pom.xml文件中添加Spring Boot的WebSocket依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 接下来,在Spring Boot的主类上添加@EnableWebSocket注解,启用WebSocket支持。 ```java @SpringBootApplication @EnableWebSocket public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 创建一个WebSocket处理类,实现WebSocketHandler接口,并重写相应的方法。 ```java @Component public class MyWebSocketHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // 当与客户端建立连接后触发 } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { // 当接收到客户端消息时触发 } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { // 当发生传输错误时触发 } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { // 当与客户端断开连接后触发 } @Override public boolean supportsPartialMessages() { return false; } } ``` 4. 在WebSocket处理类中可以利用session对象与客户端进行交互,发送消息或者接收客户端发送的消息。例如,可以在`afterConnectionEstablished`方法中使用`session.sendMessage()`方法发送欢迎消息给客户端,在`handleMessage`方法中处理客户端发送的消息。 5. 创建一个配置类来注册WebSocketHandler,并指定WebSocket的访问路径。 ```java @Configuration public class WebSocketConfig implements WebSocketConfigurer { @Autowired private MyWebSocketHandler myWebSocketHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler, "/websocket").setAllowedOrigins("*"); } } ``` 6. 在网页客户端中,可以利用JavaScript的WebSocket API来与服务端建立连接,并进行通信。 ```javascript var socket = new WebSocket('ws://localhost:8080/websocket'); socket.onopen = function() { // 当与服务端建立连接后触发 } socket.onmessage = function(event) { var message = event.data; // 接收服务端发送的消息 } socket.onclose = function(event) { // 当与服务端断开连接后触发 } function sendMessage(message) { socket.send(message); // 发送消息给服务端 } ``` 以上就是使用Spring Boot实现WebSocket服务端和网页客户端的基本步骤。可以根据实际需求,进一步细化和定制化相关功能。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值