SpringBoot如何使用WebSocket+jsch实现前后端交互获取服务器log并返回前端?

文章介绍了如何在SpringBoot应用中配置WebSocket服务端,包括依赖引入、配置类和逻辑处理。接着展示了如何使用JSch库建立SSH连接,并通过WebSocket发送消息。同时,文章提到了前端JavaScript的WebSocket工具包实现,以及在页面中调用WebSocket进行实时日志查询的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、导入依赖

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

二、新建WebSocket配置类

@Component
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();
    }
}

三、新建WebSocket服务端,在其中处理websocket逻辑

@Component  //注册到容器中
@ServerEndpoint("/webSocket")  //接收websocket请求路径
@Slf4j
public class WebSocket {
    //当前连接(每个websocket连入都会创建一个WebSocket实例)
    private Session session;

    //定义一个websocket容器存储session,即存放所有在线的socket连接
    private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();

    //处理连接建立
    @OnOpen
    public void opOpen(Session session){
        this.session = session;
        log.info("【有新的客户端连接了】:{}",session.getId());
        webSocketSet.add(this);  //将新用户加入在线组
        log.info("【websocket消息】有新的连接,总数:{}",webSocketSet.size());
    }

    //处理连接关闭
    @OnClose
    public void Onclose(){
        webSocketSet.remove(this);
        log.info("【websocket消息】连接断开,总数:{}",webSocketSet.size());
    }

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

    // 群发消息
    public void sendMessage(String message) {
        for (WebSocket webSocket : webSocketSet) {
            log.info("【websocket消息】广播群发消息,message={}",message);
            try {
                webSocket.session.getBasicRemote().sendText(message);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}
jsch连接配置

一、引入依赖

  <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.54</version>
        </dependency>

二、配置工具类,并在工具类中拉入websocket进行消息发送

注意springContextHolder的作用是websocket的特殊属性导致Autowired用不了,直接Autowired会拉入一个null对象。

@Slf4j
public class JSchUtil {

    private String ipAddress;   //主机ip
    private String username;   // 账号
    private String password;   // 密码
    private int port;  // 端口号

    Session session;

    private static WebSocket webSocket =  SpringContextHolder.getBean(WebSocket.class);

在JschUtil中的发送方法中使用websocket发送消息

  /**
     * 执行相关的命令(交互式) 并获取返回所有报文
     */
    public void execute4(String command) throws IOException {

        ChannelShell channel = null;
        PrintWriter printWriter = null;
        BufferedReader input = null;
        Vector<String> stdout  = new Vector<>();
        try {
            //建立交互式通道
            channel = (ChannelShell) session.openChannel("shell");
            channel.connect();

            //获取输入
            InputStreamReader inputStreamReader = new InputStreamReader(channel.getInputStream());
            input = new BufferedReader(inputStreamReader);

            //输出

            printWriter = new PrintWriter(channel.getOutputStream());
            printWriter.println(command);

            printWriter.println("exit");
            printWriter.flush();
//            log.info("The remote command is: ");
            System.out.println("The remote command is: ");
            String line;
            StringBuilder sb = new StringBuilder();
            Integer times = 0;
            Integer timeout=3600;

            while ((line = input.readLine()) != null) {
                if(  times<timeout){
                    Thread.sleep(700);
                    times += 1;
                    webSocket.sendMessage(line);
                }else{
                    webSocket.Onclose();
                    break;
                }
            }
            System.out.println("连接结束了");

        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }finally {
            printWriter.close();
            input.close();
            if (channel != null) {
                //关闭通道
                channel.disconnect();
            }
        }

    }

前端建设

一、写websocket.js工具包

 详细代码

/*
 * @Date: 2023-03-01 11:52:00
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: ./src/utils/websocket.js
 */
import { Message } from 'element-ui'
//import { getToken } from '@/utils/authToken' // 请求是否需要token


let websock = null
let messageCallback = null
let errorCallback = null
let wsUrl = ''
let tryTime = 0

// 接收ws后端返回的数据
export function websocketonmessage (e) {
  // messageCallback(JSON.parse(e.data))
  // console.log(messageCallback(JSON.parse(e.data)))
  // console.log(e.data);
  window.dispatchEvent(
    new CustomEvent('onmessageWS', {
      detail: {
        data: e.data
      }
    })
  );
  return e.data;
}

/**
 * 发起websocket连接
 * @param {Object} agentData 需要向后台传递的参数数据
 */
function websocketSend (agentData) {
  // 加延迟是为了尽量让ws连接状态变为OPEN
  let timer = () => {
    console.log(websock.readyState);
    // 添加状态判断,当为OPEN时,发送消息
    if (websock.readyState === websock.OPEN) { // websock.OPEN = 1
      // 发给后端的数据需要字符串化
      websock.send(JSON.stringify(agentData))
    }
    if (websock.readyState === websock.CLOSED) { // websock.CLOSED = 3
      console.log('websock.readyState=3')
      Message.error('ws连接异常,请稍候重试')
      errorCallback()
    }
  }
  setTimeout(() => {
    timer()
  }, 500)
}

// 关闭ws连接
function websocketclose (e) {
  // e.code === 1000  表示正常关闭。 无论为何目的而创建, 该链接都已成功完成任务。
  // e.code !== 1000  表示非正常关闭。
  if (e && e.code !== 1000) {
    // Message.error('ws连接异常,非正常关闭')
    // errorCallback()
    // // 如果需要设置异常重连则可替换为下面的代码,自行进行测试
    // if (tryTime < 10) {
    //   setTimeout(function() {
    //    websock = null
    //    tryTime++
    //    initWebSocket()
    //    console.log(`第${tryTime}次重连`)
    //  }, 3 * 1000)
    //} else {
    //  Message.error('重连失败!请稍后重试')
    //}
  }
}
// 建立ws连接
function websocketOpen (e) {
  // console.log('ws连接成功')
}

// 初始化weosocket
function initWebSocket () {
  if (typeof (WebSocket) === 'undefined') {
    Message.error('您的浏览器不支持WebSocket,无法获取数据')
    return false
  }

  const token = '';

  // // ws请求完整地址
  const requstWsUrl = wsUrl // + '?' + token
  websock = new WebSocket(requstWsUrl)

  // websock = new WebSocket();

  websock.onmessage = function (e) {
    websocketonmessage(e)
  }
  websock.onopen = function () {
    websocketOpen()
  }
  websock.onerror = function () {
    Message.error('ws连接异常,请稍候重试')
    // errorCallback()
  }
  websock.onclose = function (e) {
    websocketclose(e)
  }
}

/**
 * 发起websocket请求函数
 * @param {string} url ws连接地址
 * @param {Object} agentData 传给后台的参数
 * @param {function} successCallback 接收到ws数据,对数据进行处理的回调函数
 * @param {function} errCallback ws连接错误的回调函数
 */
export function sendWebsocket (url, agentData) {
  let protocol = "ws://";
  if (window.location.protocol === 'https:') {
    protocol = "wss://";
  }
  wsUrl = protocol + "localhost:8090" + url;
  initWebSocket()
  websocketSend(agentData)
}



/**
 * 关闭websocket函数
 */
export function closeWebsocket () {
  if (websock) {
    websock.close() // 关闭websocket
    websock.onclose() // 关闭websocket
  }
}

二、在main.js中注册websocket.js 

三、在前端页面中调用websocket,我这个页面在查询方法getLogContent中调用

 详细代码:

getLogContent() {
      // 防止用户多次连续点击发起请求,所以要先关闭上次的ws请求。
      this.$webSocket.closeWebsocket();
      // 发起ws请求
      // let url = 'ws://10.144.249.7:8099/monitor?logPath=/home/prep/logs/'+this.formatDate(Date.now(),'YMD')+'/PrepNormal.log';
      // 跟后端协商,需要什么参数数据给后台
      this.$message({
        message: '查询中,请稍等',
        type: 'success'
      });
      const params = {
        threadName: this.threadName,
        keyword: this.searchInput,
        viewType: this.viewType
      };
      var textarea = '';
      var statusLog = document.getElementById("log-container"); //log-container 即是页面需要展示内容的div
      // let url = 'ws://localhost:8082/ws/getLogs/'   // 本地调试
      let url = '/ws/getLogs/'   // 远程
      console.log(url);
      this.$webSocket.sendWebsocket(url, params);
      let getSocketData = e => {
        // console.log(e)
        textarea = textarea + '\n' + e.detail.data
        var ansi_up = new AnsiUp()
        var html = ansi_up.ansi_to_html(textarea);   //调用ansi_to_html()方法,txt就是从后端拿到的json数据
        statusLog.innerHTML = html;

        if (!this.mouseflag) {
          this.scrollToBottom()
        }
      };
      window.addEventListener('onmessageWS', getSocketData);
    },

 注意:AnsiUp在使用之前需要注册

(AnsiUp,经常和websocket结合使用)

1.安装

$ npm install ansi_up

2.引入

import {default as AnsiUp} from 'ansi_up';

3.使用

var ansi_up = new AnsiUp;

var html = ansi_up.ansi_to_html(txt);   //调用ansi_to_html()方法,txt就是从后端拿到的json数据

var statusLog= document.getElementById("statusLog"); //statusLog 即是页面需要展示内容的div

statusLog.innerHTML = html;

参考:

websocket后端建设:https://www.cnblogs.com/xiaozhengtongxue/p/13448778.html

AnsiUp的使用步骤:

https://blog.youkuaiyun.com/weixin_50561602/article/details/116496609

JSch连接ssh参考:

https://blog.youkuaiyun.com/cxmfly11/article/details/129217663?spm=1001.2014.3001.5501

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值