概述
- 使用qt和libvncclient编写vnc的客户端程序,多线程读写,拒绝卡顿。
- qt环境:5.15.3
- libvncclient:0.9.14
- 下载地址:https://github.com/LibVNC/libvncserver/releases
编译libvncclient
- 打开CMakeList文件,找到编译开关,注释掉不需要的编译项目:
# all the build configuration switches
option(LIBVNCSERVER_INSTALL "Generate installation target" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" ${UNIX})
option(WITH_ZLIB "Search for the zlib compression library to support additional encodings" ON)
option(WITH_LZO "Search for the LZO compression library to omit internal miniLZO implementation" ON)
option(WITH_JPEG "Search for the libjpeg compression library to support additional encodings" ON)
option(WITH_PNG "Search for the PNG compression library to support additional encodings" ON)
option(WITH_SDL "Search for the Simple Direct Media Layer library to build an example SDL vnc client" ON)
option(WITH_GTK "Search for the GTK library to build an example GTK vnc client" ON)
option(WITH_LIBSSHTUNNEL "Search for libsshtunnel to build an example ssh-tunneled client" ON)
option(WITH_THREADS "Search for a threading library to build with multithreading support" ON)
option(PREFER_WIN32THREADS "When searching for a threading library, prefer win32 threads if they are found" ON)
option(WITH_GNUTLS "Search for the GnuTLS secure communications library to support TLS" OFF)
option(WITH_OPENSSL "Search for the OpenSSL cryptography library to support TLS and use as crypto backend" ON)
option(WITH_SYSTEMD "Search for libsystemd to build with systemd socket activation support" ON)
option(WITH_GCRYPT "Search for Libgcrypt to use as crypto backend" ON)
option(WITH_FFMPEG "Search for FFMPEG to build an example VNC to MPEG encoder" ON)
option(WITH_TIGHTVNC_FILETRANSFER "Enable filetransfer if there is pthreads support" ON)
option(WITH_24BPP "Allow 24 bpp" ON)
option(WITH_IPv6 "Enable IPv6 Support" ON)
option(WITH_WEBSOCKETS "Build with websockets support" ON)
option(WITH_SASL "Build with SASL support" ON)
option(WITH_XCB "Build with XCB support" ON)
option(WITH_EXAMPLES "Build examples" OFF)
option(WITH_TESTS "Build tests" OFF)
option(WITH_QT "Build the Qt client example" OFF)
- 也可以自己手动去掉不需要的库依赖,我这里没有把server的库依赖去干净。
- 然后取出项目中的libvncclient.so和头文件中的client目录到自己的项目中。
项目思路
- 主要思路如下:
- 创建一个主窗口QWidget用于画面渲染,创建一个接收线程和一个发送线程。
- 接收线程负责读取vnc服务器发送的数据并解码装换成QImage,通过信号发送QImage,通知主线程刷新页面。
- 发送线程用于发送主窗口的鼠标移动,点击等事件,用于实时更新操作。
项目编写
- VncWideget : 主要的渲染窗口
#ifndef VNCVIEWWIDGET_H
#define VNCVIEWWIDGET_H
#include <QWidget>
#include <QThread>
#include "rfb/rfbclient.h"
#include "vnc_client/vncrecvthread.h"
#include "vnc_client/vncsendworker.h"
class VncViewWidget : public QWidget
{
Q_OBJECT
public:
explicit VncViewWidget(QString ip, quint32 port, QWidget *parent = nullptr);
~VncViewWidget();
void start();
void stop();
inline bool isStarted(){
return _startFlag;
}
void updateImage(const QImage& image);
void paintEvent(QPaintEvent *event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void closeEvent(QCloseEvent* event) override;
private:
QImage _image;
rfbClient *_cl;
QString _ip;
quint32 _port;
bool _startFlag = false;
QThread* _vncSendThread;
VNCSendWorker* _vncSendWorker;
VNCRecvThread* _vncRecvThread;
signals:
void sendMouseState(rfbClient* cl, int x, int y, int button);
void fullWindowCloseSignal();
public slots:
};
#endif // VNCVIEWWIDGET_H
- VNCSendWorker:数据发送工作线程
#ifndef VNCSENDWORKER_H
#define VNCSENDWORKER_H
#include <QObject>
#include "rfb/rfbclient.h"
class VNCSendWorker : public QObject
{
Q_OBJECT
public:
explicit VNCSendWorker( QObject *parent = nullptr);
signals:
public slots:
void sendMouseUpdateMsg(rfbClient* cl, int x, int y, int button);
};
#endif // VNCSENDWORKER_H
VNCRecvThread :接收线程
#ifndef VNCRECVTHREAD_H
#define VNCRECVTHREAD_H
#include <QThread>
#include <QImage>
#include "rfb/rfbclient.h"
class VNCRecvThread : public QThread
{
Q_OBJECT
public:
VNCRecvThread(QObject* parent = nullptr);
inline void startRun(rfbClient* cl){
if(_runFlag)return;
_cl = cl;
_cl->FinishedFrameBufferUpdate = frameBufferUpdated;
rfbClientSetClientData(_cl, nullptr, this);
_runFlag = true;
this->start();
}
inline void stopRun(){
if(!_runFlag)return;
_runFlag = false;
if(_cl)rfbClientSetClientData(_cl, nullptr, nullptr);
if(_cl)_cl->FinishedFrameBufferUpdate = nullptr;
_cl = nullptr;
}
static void frameBufferUpdated(rfbClient* cl);
protected:
void run() override;
private:
bool _runFlag = false;
rfbClient* _cl;
signals:
void updateImageSignal(QImage);
};
#endif // VNCRECVTHREAD_H
项目完整代码
gitee:https://gitee.com/li-gouhi2333/vncclient/tree/master/