跳到主要内容

Java远程桌面实现

· 阅读需 6 分钟

突发奇想,使用java是否可以实现远程共享桌面的功能呢?

远程桌面这类工具平时用得多,比如向日葵、TeamViewer,但很少有人想过它背后到底怎么跑起来的。拆开看其实就三件事:抓取屏幕画面、把数据压小、通过网络送到对端再还原。这三步每一步都有效率问题,而 Java 在第一步就先天吃亏。这篇就记录一下我用 Java 折腾远程桌面时,在这三个环节上各踩了什么坑、最后选了什么方案。

截屏效率是第一道坎

当然可以了,都是跑代码,但是java效率比较低,为啥,也不是因为java代码执行效率低下,而是因为出于Java自带的屏幕截屏工具效率过低,java提供了robot类来实现桌面截屏,2k屏幕一张jpg大小在200kb左右,一张截图我使用的rx2700x cpu 一张截图竟然需要30毫秒 距离1秒30帧率还是差距有的,更别提1秒60帧率。就算如果1秒60帧率 如何使用 java 高效的传输到各个远程端呢 ?

补充一下机制层面的原因。java.awt.RobotcreateScreenCapture 走的是 AWT 的本地调用,底层会把整块屏幕的像素从显存复制到内存,再封装成 BufferedImage。这个过程是同步的全量拷贝,分辨率越高拷贝的数据量越大,2K 屏一帧的原始 RGB 数据就有十几 MB,光是这一次内存搬运就注定快不了。它的设计初衷是给 GUI 自动化测试偶尔截一张图用的,不是为持续高频采集准备的。

有人会说使用多线程截图,来提高帧率,这样的做法,我个人觉得不太科学,为啥?因为占用太高cpu资源,应该寻找高效并占用cpu资源较少的方法。

多线程还有一个隐性问题:截屏的瓶颈在系统调用和内存拷贝上,多个线程同时抢这条通道,吞吐未必线性上涨,CPU 占用倒是实打实翻倍。远程桌面是常驻后台的工具,把宿主机 CPU 吃满,本身就不可接受。

图片直传行不通

并且图片传输不可取,因为一张图片太大 ,200kb,1s 30张就是 6000kb 严重占用带宽。

算一下就知道这条路走不通:6000kb 每秒,换算成带宽接近 50Mbps,家用上行根本扛不住,更别说一台主机同时给多个远程端推流。而且 JPEG 每一帧都是独立压缩,前后两帧就算 99% 的像素没变,也照样把整张图编码一遍再发一遍,浪费在了大量重复信息上。

视频流是正路

最好的方式使用视频流,使用开源的h.264视频格式,还有更好的h.265 但是我并没有找到对应的开源jar。h.265技术还处于收费阶段。h.264视频格式 为根据每一帧中的像素点变化来记录其数据。而不需要记录整个图片中绝大部分像素信息。所以h.264格式的视频体积远远小于图片传输的数据总和。更加利于网络传输。

这正是视频编码里帧间压缩的思路:编码器隔一段发一个完整的关键帧,中间的帧只记录相对前一帧的变化量。桌面画面恰好是这种压缩的理想场景——大部分时间只有鼠标和局部窗口在动,背景纹丝不动,帧间冗余极高,压缩比自然可观。

通过录制桌面的方式,有效推流到客户端,可以大大降低图片带来的带宽和截屏的效率问题,但是使用视频流的话,实时转码与流量都是一个不小的挑战。

实时转码的难点在于延迟和算力的平衡:压缩率调高,编码耗时就上去了,远程操作最怕的就是画面比手慢半拍;压缩率调低,带宽又顶不住。成熟的远程桌面软件一般靠硬件编码来解这个矛盾,纯 Java 想在软件层面追上,难度不小。

我最终的折中方案

最终我采用的是行程编码,并只传输 robot 截图 rgb 位图中有改变的数据。但行程编码在遇到屏幕像素变化太大的情况 数据量体积急剧上升。我认为如果真要用图片来进行传输桌面信息。需要突破java获取的屏幕像素数据效率以及数据传输的体积压缩最小化,才能保证远程桌面的帧率最高。

展开说一下这个方案。做法是把当前帧和上一帧的 RGB 数组逐像素比对,只把变化的像素连同位置信息编码后发出去,对端拿到差量数据后覆盖到本地缓存的画面上。行程编码(RLE)本身很简单:连续相同的值记成「值 + 重复次数」,静止画面下差量几乎为零,效果很好。但它的弱点也明显——一旦全屏滚动或者播放视频,几乎每个像素都在变,差量退化成全量,再叠加编码本身的开销,数据量反而可能超过直接发原图。

踩坑与注意

1)Robot 截屏的耗时和分辨率直接挂钩,测试时用小分辨率看着帧率还行,换到 2K 屏立刻现原形,评估方案时要按目标分辨率来测。

2)差量传输必须处理好首帧和丢包:对端没有基准画面时差量毫无意义,需要先发一次全量帧;传输中途丢了一帧差量,后面的画面就会一直错位,要么用可靠传输,要么定期强制刷全量帧兜底。

3)别忽视对端的还原成本,把差量合并回位图再绘制到界面上同样吃 CPU,接收端太弱一样会卡。

小结

这次尝试下来,结论比较清楚:Java 做远程桌面,瓶颈不在语言本身,而在 Robot 这条截屏路径的效率和纯软件压缩的开销。图片直传带宽爆炸,行程编码差量传输适合静态画面但扛不住剧烈变化,视频流才是工程上的正解,只是纯 Java 生态里缺少顺手的 h.264 开源实现。如果只是做个玩具验证原理,差量传输够用了;真要做产品,还是得借助本地编码库或硬件编码的能力。

评论 / COMMENTS