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

文章目录

  • 前言
  • 一、先创建好SpringBoot框架
  • 二、使用步骤
    • 1.使用maven引入依赖
    • 2.创建服务端
      • 创建WebSocketServer
      • 创建WebSocketConfig
    • 3.创建客户端-web版本
      • web版连接演示
    • 4.SpringBoot作为客户端 带断线重连
      • 1.创建MyWebSocketClient
      • 2.新建工具类解析ByteBuffer 数据 ByteUtils
      • 新建WebSocketConfig
      • 4.演示
  • 总结


前言

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

SpringBoot中使用Websocket


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

一、先创建好SpringBoot框架

二、使用步骤

1.使用maven引入依赖

代码如下:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-websocketartifactId>
dependency>
<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
dependency>
 <dependency>
 <groupId>org.apache.commonsgroupId>
     <artifactId>commons-lang3artifactId>
     <version>3.8.1version>
 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()">开启socketa>div>
<p>【操作】:
<div><a onclick="sendMessage()">发送消息a>div>
body>

html>

html>

web版连接演示

在这里插入图片描述
Springboot使用websocket服务端+客户端(断线重连)_第1张图片
Springboot使用websocket服务端+客户端(断线重连)_第2张图片

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

新建SpringBoot项目


<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <optional>trueoptional>
dependency>
<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.7.22version>
dependency>
<dependency>
    <groupId>org.java-websocketgroupId>
    <artifactId>Java-WebSocketartifactId>
    <version>1.5.2version>
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.演示

Springboot使用websocket服务端+客户端(断线重连)_第3张图片
服务端

总结

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

你可能感兴趣的:(java,java,springboot,websocket)