Springboot实现VNC的反向代理

2023-05-16

背景

用户需要通过前端HTML页面的noVNC(noVNC是什么?)客户端连接底层VNC Server服务端,为了防止VNC Server的IP暴露,因此需要做一层代理。正常情况下使用Nginx、Apache等都可以搞定,但是由于项目架构的一些问题,暂时不能再加一台反向代理服务器,所以决定写一个单独的模块实现反向代理的功能。

在网上和Github上找了一下,使用了HTTP-Proxy-Servlet,引入该依赖搭建一个Spring Boot项目。

搭建

1.引入代理的依赖

<dependency><groupId>org.mitre.dsmiley.httpproxy</groupId><artifactId>smiley-http-proxy-servlet</artifactId><version>1.12</version></dependency>

2.通过注册bean拦截指定URL路径进行自定义操作

@ConfigurationpublicclassProxyServletConfiguration{

    // 拦截所有请求交给下面的VNCProxyServlet去处理privatefinalstatic String SERVLET_URL = "/*";

    @Beanpublic ServletRegistrationBean<VNCProxyServlet> servletServletRegistrationBean(){
        ServletRegistrationBean<VNCProxyServlet> servletRegistrationBean = new ServletRegistrationBean<>(new VNCProxyServlet(), SERVLET_URL);
        //设置网址以及参数
        Map<String, String> params = ImmutableMap.of(
                "targetUri", "null", //这里写null是因为targetUri是在自定义的VNCProxyServlet类中动态传入的,而且这里必须要有值
                ProxyServlet.P_LOG, "true",
                ProxyServlet.P_PRESERVEHOST,"true",
                ProxyServlet.P_PRESERVECOOKIES,"true"
        );
        servletRegistrationBean.setInitParameters(params);
        return servletRegistrationBean;
    }

}

这里遇到的坑:

刚开始其实是准备在已有的一个模块中加上这个代理功能,因为可以指定拦截的路径,比如只拦截请求路径为/proxy/*的,然后交给自定义的Servlet去代理,后来写好测试时,发现代理过去后代理目标主页一片空白,看了控制台的Network后,主页确实是返回200且加载正常,但是由主页发起的js、css和img等静态资源状态码都为404。

当时以为是代码的问题,后来发现静态资源都是相对路径的有问题,如果前端的静态资源是引入第三方的,比如从CDN中引入Vue.js则不会出现问题,都可以正常的被代理。既然状态码是404,那肯定是找不到这个资源,看了一下发现如果在静态资源的路径前加上指定拦截的路径/proxy/就可以被正常代理。此时才明白,因为访问首页的路径中带/proxy/是在地址栏主动输入的,所以请求到后台,后台Servlet拦截发现路径中带/proxy/,把该请求交给自定义的代理Servlet去处理然后返回。而主页上的js、css等静态资源发起请求的路径是不会带/proxy/*的,因此不会走到代理Servlet,并且代理模块中也没有相应资源路径,所以就理所应当的返回了404。

为此还专门在GitHub上问了一下作者,作者也是回复说这并不是这个代理模块该做的事,最好是前端处理,或者让前端使用绝对路径。附上地址(Discussions)

最后就是决定单独拉出来写一个Spring Boot项目做这个代理功能模块,直接代理/*,这样所有请求到这个模块的都会被代理。

3.自定义Servlet实现动态代理目标地址

// VNCProxyServlet继承了ProxyServlet 重写了service方法 在方法中添加自定义操作 从请求地址中动态获取@Overrideprotectedvoidservice(HttpServletRequest servletRequest, HttpServletResponse servletResponse)throws ServletException, IOException {
        // 获取请求地址
        String targetUri = servletRequest.getRequestURL().toString();
		
        // 正则取请求地址中的参数 参数是放在域名中的
        Matcher matcher = DOMAIN_REX.matcher(targetUri);
        if(!matcher.find()){
            // 自定义的异常thrownew GenericException("从域名中获取vmId异常!");
        }
        // 取域名中的第一个 eg: http://vmId.xxx.cn得 [vmId,xxx,cn] 得 vmId
        Long vmId = Long.valueOf(matcher.group().split("\\.")[0]);
        
        // eg:业务逻辑根据vmId去拿 targetUri
    	targetUri = vmService.getTargetUrl(vmId);
	
        if (StringUtils.isEmpty(targetUri)) {
            thrownew GenericException("代理路径不正确,请确认路径");
        }

        // 设置Urlif (servletRequest.getAttribute(ATTR_TARGET_URI) == null) {
            servletRequest.setAttribute(ATTR_TARGET_URI, targetUri);
        }

        // 设置Hostif (servletRequest.getAttribute(ATTR_TARGET_HOST) == null) {
            URL trueUrl = URLUtil.url(targetUri);
            servletRequest.setAttribute(ATTR_TARGET_HOST, new HttpHost(trueUrl.getHost(), trueUrl.getPort(), trueUrl.getProtocol()));
        }
		// 下面大部分都是父类的源码 没有需要特别修改的地方
        String method = servletRequest.getMethod();
        // 替换多余路径
        String proxyRequestUri = this.rewriteUrlFromRequest(servletRequest);

        HttpRequest proxyRequest;
        if (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) != null ||
                servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != null) {
            proxyRequest = new BasicHttpRequest(method, proxyRequestUri);
        } else {
            proxyRequest = this.newProxyRequestWithEntity(method, proxyRequestUri, servletRequest);
        }

        this.copyRequestHeaders(servletRequest, proxyRequest);
        setXForwardedForHeader(servletRequest, proxyRequest);
        HttpResponse proxyResponse = null;

        try {
            // Execute the request
            proxyResponse = this.doExecute(servletRequest, servletResponse, proxyRequest);

            // Process the responseint statusCode = proxyResponse.getStatusLine().getStatusCode();

            // "reason phrase" is deprecated but it's the only way to pass
            servletResponse.setStatus(statusCode, proxyResponse.getStatusLine().getReasonPhrase());

            // copying response headers to make sure SESSIONID or other Cookie which comes from remote server// will be saved in client when the proxied url was redirected to another one.
            copyResponseHeaders(proxyResponse, servletRequest, servletResponse);

            if (statusCode == HttpServletResponse.SC_NOT_MODIFIED) {
                servletResponse.setIntHeader(HttpHeaders.CONTENT_LENGTH, 0);
            } else {
                copyResponseEntity(proxyResponse, servletResponse, proxyRequest, servletRequest);
            }
        } catch (Exception e) {
            handleRequestException(proxyRequest, proxyResponse, e);
        } finally {
            if (proxyResponse != null) {
                EntityUtils.consumeQuietly(proxyResponse.getEntity());
            }
        }
    }

这里主要列出关键部分,因为方案还没有完结。

问题

本以为这样就成功了,但是测试之后发现页面和静态资源都代理过去了,但是有一个websocket请求失败了。像noVNC这种网页版的黑窗口,早就该想到肯定是用websocket这种长链接的请求进行交互的。后来去搜了一下这个叫websockify的请求,就是最开始介绍noVNC博客中介绍的:

浏览器不支持VNC,所以不能直接连接VNC,但是可以使用代理,使用noVNC通过WebSocket建立连接,而VNC Server不支持WebSocket,所以需要开启Websockify代理来做WebSocket和TCP Socket之间的转换,这个代理在noVNC的目录里,叫做websockify。
此时项目是能够拦截到websockify这个请求的,但是由于servlet把这个请求当成普通的请求去代理到目标服务器,这样是无法成功的,所以要做的就是类似实现一个websocket的反向代理,搜了一下的话发现例子不是很多,大多都是在前端做的,前端作为客户端与服务端建立websocket连接,但目前的状况很明显是需要这个代理模块既做websocket服务端与web端建立连接,再作为websocket客户端与VNC 服务端建立连接,然后进行交互传递通信。

后面也找到了这篇博客通过noVNC和websockify连接到QEMU/KVM,然后总结一下从用户发出请求到得到响应的流程:

PC Chrome(客户端) => noVNC Server(noVNC端) => websockify(websocket转TCP Socket) => VNC Server(VNC服务端) => websockify(TCP Socket转websocket) => noVNC Server(noVNC端)=> PC Chrome(客户端)

用户使用PC Chrome浏览器请求 noVNC端(因为无法直接访问VNC Server端,VNC Server是不支持Websocket连接),经由websockify将websocket转为TCP Socket请求到VNC服务端,返回TCP响应,经由websockify转换为websocket返回给客户端浏览器,这样来进行交互。整个过程 websockify 代理器是关键,noVNC 可以被放在浏览器端

1.noVNC网页端与代理模块建立websocket通信

@ConfigurationpublicclassWebSocketConfig{
    @Beanpublic ServerEndpointExporter serverEndpointExporter(){
        returnnew ServerEndpointExporter();
    }
}
@ServerEndpoint("/websockify")
@Component
public class WebSocketServer {

    /**
     * 连接建立成功调用的方法
     */@OnOpen
    public void onOpen(Session session) {
        logger.info("open...");
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClosepublicvoidonClose() {
        logger.info("close...");
    }

    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessagepublicvoidonMessage(String message, Session session) {
        logger.info(message);
    }

    /**
     * 发生错误调用的方法
     */
    @OnErrorpublicvoidonError(Session session, Throwable error) {
        logger.error("用户错误原因:"+error.getMessage());
        error.printStackTrace();
    }

}

都是很常用的websocket服务端的代码,唯一要注意的是前端请求'/websockify'地址发起websocket连接时,要注意用ip,尤其是本地,使用localhost会报错,要使用127.0.0.1。最后测试连接成功,返回状态码101,并且消息可以正常接收。noVNC网页端与代理模块建立websocket通信完成。

2.代理模块与VNC Server建立websocket通信

java后台作为websocket客户端很少,大多是用Netty去写的,但是不适合目前的情况,最后还是找到了一个感觉比较合适的

publicclassMyWebSocketClient {
    publicstatic WebSocketClient mWs;
    publicstaticvoidmain(String[] args) {
        try {
            // 
            String url = "ws://172.28.132.11:8888/websocketify";
            URI uri = new URI(url);
            HashMap<String, String> httpHeadersMap = new HashMap<>();
            httpHeadersMap.put("Sec-WebSocket-Version", "13");
            httpHeadersMap.put("Sec-WebSocket-Key", "YBhzbbwLI83U5EH8Tlutwg==");
            httpHeadersMap.put("Connection","Upgrade");
            httpHeadersMap.put("Upgrade","websocket");
            httpHeadersMap.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36");
            httpHeadersMap.put("Cookie","token=8asda2das-84easdac-asdaqwe4-2asda-asdsadas");
            httpHeadersMap.put("Sec-WebSocket-Extensions","permessage-deflate; client_max_window_bits");
            mWs = new WebSocketClient(uri,httpHeadersMap){
                @Override
                publicvoidonOpen(ServerHandshake serverHandshake) {
                    System.out.println("open...");
                    System.out.println(serverHandshake.getHttpStatus());
                    mWs.send("666");
                }

                @Override
                publicvoidonMessage(String s) {
                    System.out.println(s);
                }

                @Override
                publicvoidonClose(int i, String s, boolean b) {
                    System.out.println("close...");
                    System.out.println(i);
                    System.out.println(s);
                    System.out.println(b);
                }

                @Override
                publicvoidonError(Exception e) {
                    System.out.println("发生了错误...");
                }
            };
            mWs.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

// 调用后报错  直接关闭了连接  状态码为1002 // close...// 1002// Invalid status code received: 400 Status line: HTTP/1.1 400 Client must support 'binary' or 'base64' protocol

发生错误后,发现关键地方,客户端必须支持 binary或base64协议,一番搜索后再Stack Overflow找到了线索,并且是Kanaka(noVNC和websockify的开发者)亲自回答的,大概意思就是拟需要在构造函数中提供这些协议。

然后我又在websockify.js的源码中找到了这个构造,确实需要传递一个protocols的数组参数,可是这是前端,并不知道Java如何完成这个操作。

后续

首先再次感谢开源项目和各位博主大佬的分享,依旧在寻找解决方案......

资源获取:
大家 点赞、收藏、关注、评论啦 、 查看 👇🏻 👇🏻 👇🏻 微信公众号获取联系方式 👇🏻 👇🏻 👇🏻
精彩专栏推荐订阅:下方专栏 👇🏻 👇🏻 👇🏻 👇🏻
每天学四小时:Java+Spring+JVM+分布式高并发,架构师指日可待
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springboot实现VNC的反向代理 的相关文章

  • C++学习日记——头文件的编写

    目录 1 头文件概述 2 头文件编写格式及要求 3 头文件引用的源文件编写要求 4 主函数的调用 5 其他函数 6 放松时刻 1 头文件概述 对于一些大型程序而言 xff0c 分文件编写尤为重要 xff0c 进而引申出了编写头文件的相关需求
  • 网络基础-linux应用编程和网络编程第8部分-朱有鹏-专题视频课程

    网络基础 linux应用编程和网络编程第8部分 4548人已学习 课程介绍 本课程是网络编程部分的前奏 xff0c 主要讲解了网络相关的一些基础知识 xff0c 譬如网络编程的架构 xff0c 网卡 路由器 集线器 交换机 DHCP NAT
  • Ubuntu 20.04 LTS安装教程

    下载ubuntu 20 04 desktop amd64 iso系统镜像 xff0c 利用软碟通写入U盘或是刻录光盘启动安装 xff08 此次安装环境为联想ThinkPad E570 xff0c 内存为500G 43 128G xff09
  • TypeScript

    什么是TypeScript typescript是拥有类型的javaScript超集 它可以编译成普通 干净 完整的js代码 ES3 js所拥有的内容ts都支持 并且js有es678 ts也都支持这些语法 ts拥有独特的一些数据类型 枚举
  • VNC远程连接树莓派的IP问题

    在进行VNC远程连接树莓派的时候 xff0c 需要输入树莓派的IP地址 xff0c 我首先使用了 hostname i的指令 xff0c 但是这个显示连接失败 xff0c 拒绝访问 xff0c 后来发现hostname i是获取了主机名 正
  • VNC链接和配置

    1 创建vnc进程ID xff1a vnc4server 2 关掉vnc进程 xff1a vncserver kill lt portnum gt vncserver kill 28 3 设置vnc分辨率 xff1a vnc4server
  • Ubuntu系统下Python基础库的安装以及pip和pip3常见报错解答

    Ubuntu系统下Python基础库的安装以及pip和pip3常见报错解答 安装python依赖库Python基础库安装Python项目requirements文件的生成和使用1 生成requirements txt方法一 xff1a pi
  • 嵌入式服务器boa移植

    移植嵌入式服务器boa的过程 xff0c 在论坛里面可以搜到好多 xff0c 其中也会有出现错误时对应的解决方法 xff0c 在这里就不赘述了 在这里我介绍一下我移植过程中出现的问题 xff1a boa not found 总结一下这个问题
  • 【深度学习-tensorflow】使用Tensorflow Lite部署模型时遇到不支持tf.keras.layers.UpSampling2D()函数问题的解决方法

    在工作中遇到的一个问题 xff0c 记录一下 在模型转化为tflite之后 xff0c 进行模型的部署 xff0c 之后遇到的使用Tensorflow Lite部署模型时遇到不支持tf keras layers UpSampling2D 函
  • Anaconda3安装好后“pip、python不是内部或外部命令,也不是可运行的程序或批处理文件”的问题解决

    这种情况就是环境配置的问题 xff0c 系统找不到你的python和pip xff0c 因此需要配置相应的环境变量 重点来了 xff01 xff01 xff01 相信很多人说 xff0c 我明明配置好了环境变量为什么还是显示错误 xff1f
  • 解决Maven的project标签(爆红) 插架无法导入

    目录 问题描述 解决方案 1 利用开发工具 idea 自动导入 2 pom依赖自动导入 错误原因 解决问题 3 网站下载插件 4 去本地仓库解决插件报红状态 4 1错误原因 4 2解决方法 问题描述 在拉取项目的时候project标签报错
  • typora全局替换文本

    主要讲解在typora中如何将整个文档中所有的字段替换为另一个字段 例如对于下面的句子 xff0c 需要把 曲面重建 全部改为 曲面三维重建 首先按ctrl 43 f打开全局搜索框 xff0c 在最顶上会弹出 xff0c 在这里输入需要替换
  • linux网络编程实践-linux应用编程和网络编程第9部分-朱有鹏-专题视频课程

    linux网络编程实践 linux应用编程和网络编程第9部分 14177人已学习 课程介绍 本课程是网络编程实践部分 xff0c 带大家使用socket接口及其相关函数 xff0c 从头编写一个服务器和客户端的通信程序 xff0c 并且引出
  • 链路聚合的原理以及配置

    链路聚合的原理以及配置 一 链路聚合的概述二 链路聚合的原理三 链路聚合的配置 一 链路聚合的概述 链路聚合 xff08 Link Aggregation xff09 是将两个或更多数据信道结合成一个单个的信道 xff0c 该信道以一个单个
  • 关于逻辑回归完成手写数字识别的一点愚蠢错误回顾

    最近的机器学习课上作业里要我们完成通过神经网络和逻辑回归进行手写数字的识别任务 xff0c 神经网络的部分通过课上的辅助教材 xff1a 神经网络与深度学习 其中所提供的代码足以完成 xff0c 另外这本书真的写的很好 xff0c 在易读性
  • OpenCV安装及其开发环境配置(C++)

    目录 第一章 Opencv安装及其环境变量配置 1 1下载并安装OpenCV 1 2 OpenCV环境变量配置 第二章 Visual Studio 2019 编译器下载安装 第三章 OpenCV开发环境配置 C 43 43 3 1创建项目
  • 目标检测中的anchor-base与anchor-free

    前言 本文参考目标检测阵营 Anchor Base vs Anchor Free 如何评价zhangshifeng最新的讨论anchor based free的论文 知乎 基础知识 目标检测中Anchor的认识及理解 目标检测领域的发展从a
  • 【论文合集】RGBD Semantic Segmentation

    来源 xff1a GitHub Yangzhangcst RGBD semantic segmentation A paper list of RGBD semantic segmentation processing RGBD seman
  • webpack实战(巨详细)

    先附上官网地址 xff1a 概念 webpack 中文网 了解下概念后 xff0c 一起开始webpack之旅吧 一 开始 首先在安装之前 xff0c 需要安装node xff0c 因为webpack的运行是依赖Node环境的 xff0c
  • qcow2格式的虚拟机镜像转换为vdi格式

    VirtualBox不能直接操作qcow2镜像 xff0c 在Windows上暂时没有找到其他替代工具 xff0c 还是需要先有一个CentOS虚拟机来运行qemu img命令 将qcw2的镜像导入或通过挂载的方式传输到Centos的虚拟机

随机推荐

  • 简单的彩票小程序(双色球含机选及自选)

    话不多说直接上代码 package com import java util public class curriculum public static void main String args 机选 int times 61 100 L
  • IDEA设置JDK版本

    idea更换版本需全更改一致 鼠标点击file gt setting xff0c 进入idea的设置页面settings xff0c 根据截图操作 然后 xff0c 鼠标点击file gt Project Structure 1 7版本 1
  • 3步教会你使用VNC Viewer远程连接控制树莓派桌面(详细)

    目录 1 查询IP地址 1 1 路由器后台查询 1 2 树莓派内查询 2 开启VNC服务 3 打开VNC Viewer进行连接 1 查询IP地址 1 1 路由器后台查询 打开路由器后台 xff0c 我这以腾达为例 xff0c 在浏览器输入
  • 驱动应该怎么学-linux驱动开发第1部分-朱有鹏-专题视频课程

    驱动应该怎么学 linux驱动开发第1部分 4059人已学习 课程介绍 本课程是linux驱动开发的第一个课程 xff0c 主要介绍linux驱动的概念 模块化设计理念 分类 安全性要求 xff0c 后讲解了linux驱动课程的整体学习方法
  • 安装DEV-C++5.11(无捆绑)

    安装网址 或者点这儿直接下载 xff09 DEV C 43 43 https pc qq com detail 16 detail 163136 html https pc qq com detail 16 detail 163136 ht
  • 中国大学慕课第7周测验

    1 单选 1分 写出下面程序的运行结果 include lt stdio h gt void Bin int x if x 2 gt 0 Bin x 2 printf 34 d n 34 x 2 int main Bin 12 return
  • 基于深度强化学习的智能船舶航迹跟踪控制

    基于深度强化学习的智能船舶航迹跟踪控制 人工智能技术与咨询 昨天 本文来自 中国舰船研究 xff0c 作者祝亢等 关注微信公众号 xff1a 人工智能技术与咨询 了解更多咨询 xff01 0 引 言 目前 xff0c 国内外对运载工具的研究
  • 面向区块链的高效物化视图维护和可信查询

    面向区块链的高效物化视图维护和可信查询 人工智能技术与咨询 来源 xff1a 软件学报 xff0c 作者蔡 磊等 摘 要 区块链具有去中心化 不可篡改和可追溯等特性 可应用于金融 物流等诸多行业 由于所有交易数据按照交易时间顺序存储在各个区
  • 基于迁移深度学习的雷达信号分选识别

    基于迁移深度学习的雷达信号分选识别 人工智能技术与咨询 来源 xff1a 软件学报 xff0c 作者王功明等 摘要 针对当前雷达信号分选识别算法普遍存在的低信噪比下识别能力差 特征参数提取困难 分类器模型参数复杂等问题 xff0c 提出了一
  • 基于深度学习的磁环表面缺陷检测算法

    基于深度学习的磁环表面缺陷检测算法 人工智能技术与咨询 来源 xff1a 人工智能与机器人研究 xff0c 作者罗菁等 关键词 缺陷检测 xff1b 深度学习 xff1b 磁环 xff1b YOLOv3 xff1b 摘要 在磁环的生产制造过
  • 基于PX4的地面无人车避障系统及路径规划研究

    基于PX4的地面无人车避障系统及路径规划研究 人工智能技术与咨询 来源 xff1a 动力系统与控制 xff0c 作者姜琼阁等 关键词 地面无人车 xff1b 避障 xff1b PX4 xff1b 摘要 地面无人车避障及路径规划是指 xff0
  • 基于图像的数据增强方法发展现状综述

    基于图像的数据增强方法发展现状综述 人工智能技术与咨询 2022 03 22 20 57 点击蓝字 关注我们 来源 xff1a 计算机科学与应用 xff0c 作者冯晓硕等 关键词 数据增强 xff1b 图像数据集 xff1b 图像处理 xf
  • 基于改进SSD算法的小目标检测与应用

    人工智能技术与咨询 点击蓝字 关注我们 来源 xff1a 计算机科学与应用 xff0c 作者刘洋等 关键词 SSD xff1b 深度学习 xff1b 小目标检测 摘要 xff1a 摘要 针对通用目标检测方法在复杂环境下检测小目标时效果不佳
  • 组网雷达融合处理组件化设计与仿真

    人工智能技术与咨询 点击蓝色 关注我们 关键词 xff1a 组网雷达 点迹融合 航迹融合 组件化设计 仿真 摘要 数据融合处理是多雷达组网的核心 以典型防空雷达网为参考对象 xff0c 采用组件化设计方式 xff0c 将组网数据融合处理过程
  • 字符设备驱动基础-linux驱动开发第2部分-朱有鹏-专题视频课程

    字符设备驱动基础 linux驱动开发第2部分 5673人已学习 课程介绍 本课程是linux驱动开发的第2个课程 xff0c 从零开始带领大家逐渐熟悉内核模块 xff0c 并且一步步写出一个字符设备驱动程序来控制LED等 本课程对驱动的学习
  • 人工智能 知识图谱

    关于举办 2022年数字信息化培训项目系列 知识图谱Knowledge Graph构建与应用研修班线上课程的通知 各有关单位 一 培训目标 本次课程安排紧密结合理论与实践 xff0c 深入浅出 xff0c 循序渐进 从基本概念讲起 xff0
  • 深度学习(Deep Learning)

    知识关键点 1 人工智能 深度学习的发展历程 2 深度学习框架 3 神经网络训练方法 4 卷积神经网络 xff0c 卷积核 池化 通道 激活函数 5 循环神经网络 xff0c 长短时记忆 LSTM 门控循环单元 GRU 6 参数初始化方法
  • 基于深度学习的机器人目标识别和跟踪

    如今 xff0c 深度学习算法的发展越来越迅速 xff0c 并且在图像处理以及目标对象识别方面已经得到了较为显著的突破 xff0c 无论是对检测对象的类型判断 xff0c 亦或者对检测对象所处方位的检测 xff0c 深度学习算法都取得了远超
  • 散列表与探测法

    动态查找的时候 如果用查找树同时对俩个变量名 字符串 进行查找 会导致效率不高的问题 引入散列的思想 把字符串变成数字 使得对字符串的比较变成对数字的比较 查找方式时间复杂度顺序查找O n 二分查找 静态查找 O log2N 二叉搜索树O
  • Springboot实现VNC的反向代理

    背景 用户需要通过前端HTML页面的noVNC xff08 noVNC是什么 xff1f xff09 客户端连接底层VNC Server服务端 xff0c 为了防止VNC Server的IP暴露 xff0c 因此需要做一层代理 正常情况下使