Verwenden Sie die libvncserver-Bibliothek, um schnell einen VNC-Server zu erstellen

Was ist VNC?

VNC (Virtual Network Computing) ist eine Software zur Bildschirmfreigabe und Fernsteuerung, die Remote Frame Buffer (RFB) verwendet. Der VNC-Server kann Desktop-Videobilder über das RFP-Protokoll ins Internet exportieren, und auf die ins Internet exportierten Videobilder kann mit dem VNC-Client zugegriffen werden. Wenn der VNC-Server Websockets ermöglicht, können wir zusätzlich zum VNC-Viewer auch über den VNC-Viewer (z. B. noVNC) im Browser eine Verbindung herstellen und darauf zugreifen.

VNC folgt im Allgemeinen der C/S-Architektur. Es lässt sich also in folgenden Punkten zusammenfassen:

  • VNC-Server, teilen Sie den Bildschirm der gesteuerten Maschine, die passiv vom VNC-Client gesteuert wird
  • Der VNC-Client, manchmal auch VNC Viewer genannt, kann den vom Server gesteuerten Bildschirm beobachten und den Server aus der Ferne bedienen
  • Genauer gesagt ist das VNC-Protokoll das RFB-Protokoll. Ein Remote-Desktop, der dieses Protokoll nicht verwendet, wird nicht als VNC bezeichnet. Der Zweck dieses Protokolls ist sehr einfach. x, y-Koordinatensystem) und Ereignismeldungen (Mausmeldungen, Tastatur). Nachrichten) werden vom Server zum Client übertragen.

libvncserver ist eine Open-Source-VNC-Serverbibliothek, die in C/C++-Anwendungen integriert werden kann, um Remotebenutzern die Anzeige und Steuerung des lokalen Computers über den VNC-Client zu ermöglichen. libvncserver bietet einige leistungsstarke APIs, die problemlos in andere Anwendungen integriert werden können. Es unterstützt mehrere Betriebssystemplattformen, darunter Linux, Windows, Mac OS X und FreeBSD usw., und bietet eine Vielzahl von VNC-Protokollimplementierungen. Mit libvncserver können Entwickler ganz einfach ihren eigenen VNC-Server erstellen, der in verschiedenen Anwendungsszenarien wie Remote-Desktop, Remote-Support und Überwachung verwendet werden kann. Gleichzeitig bietet libvncserver auch einige erweiterte Funktionen wie SFTP- und SSH-Tunnel und unterstützt SSL-Verschlüsselung mit hoher Sicherheit.

Vor- und Nachteile von libvncserver

libvncserver ist eine leistungsstarke serverseitige VNC-Bibliothek, die die folgenden Vorteile bietet:
1. Open Source und kostenlos: libvncserver verwendet die GPL-Lizenz für Open Source, die kostenlos verwendet, geändert und verteilt werden kann.
2. Starke Portabilität: libvncserver unterstützt mehrere Betriebssystemplattformen, einschließlich Linux, Windows, Mac OS X und FreeBSD usw., und verfügt über eine gute Portabilität.
3. Gute Flexibilität: libvncserver bietet einige leistungsstarke APIs, die problemlos in andere Anwendungen integriert werden können, und unterstützt auch die Implementierung mehrerer VNC-Protokolle.
4. Vollständige erweiterte Funktionen: libvncserver bietet einige erweiterte Funktionen wie SFTP- und SSH-Tunnel, SSL-Verschlüsselung usw., die die Sicherheit von VNC verbessern können.

Allerdings hat libvncserver auch die folgenden Nachteile:
1. Relativ kompliziert zu verwenden: libvncserver ist eine programmierbare Bibliothek, die relativ kompliziert zu verwenden ist und von Entwicklern entsprechende Programmiererfahrung und -fähigkeiten erfordert.
2. Die Community ist relativ klein: Im Vergleich zu anderen ähnlichen serverseitigen VNC-Bibliotheken ist die Community von libvncserver relativ klein, was für Anfänger möglicherweise schwierig zu erlernen ist.
3. Nicht für die Bereitstellung in großem Maßstab geeignet: Da libvncserver eine serverseitige VNC-Bibliothek ist, kann sie nicht direkt für die Bereitstellung in großem Maßstab von VNC verwendet werden, sondern muss in Kombination mit anderen Tools oder Anwendungen implementiert werden.
4. Das Pasteboard der Fernbedienung unterstützt das Kopieren und Einfügen von Unicode-Text nicht und kann keine anderen Zeichensatzcodes als den Latin-1-Zeichensatz übertragen.
5. Zu beachten ist, dass libvncserver, ein Open-Source-VNC-Framework, immer noch viele Fehler aufweist.

Erstellen Sie libvncserver

Projektadresse:
https://github.com/LibVNC/libvncserver

# 下载代码并构建
git clone  https://github.com/LibVNC/libvncserver  
cd libvncserver
mkdir build
cd build
cmake ..
cmake --build .

Informationen zum erstellten libvncserver finden Sie in den nachfolgenden CSDN-Downloadressourcen

Verwenden Sie libvncserver, um VNCServer zu erstellen

//引用libvncserver  用于启用服务端
#include <rfb/rfb.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>

//引用X11库用于抓取桌面,模拟鼠标键盘操作
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>

using namespace std;
static rfbCursorPtr myCursor;

//用于绘制鼠标焦点
static const char* cur=
        "                   "
        " x                 "
        " xx                "
        " xxx               "
        " xxxx              "
        " xxxxx             "
        " xxxxxx            "
        " xxxxxxx           "
        " xxxxxxxx          "
        " xxxxxxxxx         "
        " xxxxxxxxxx        "
        " xxxxx             "
        " xx xxx            "
        " x  xxx            "
        "     xxx           "
        "     xxx           "
        "      xxx          "
        "      xxx          "
        "                   ";

static const char* mask=
        "xx                 "
        "xxx                "
        "xxxx               "
        "xxxxx              "
        "xxxxxx             "
        "xxxxxxx            "
        "xxxxxxxx           "
        "xxxxxxxxx          "
        "xxxxxxxxxx         "
        "xxxxxxxxxxx        "
        "xxxxxxxxxxxx       "
        "xxxxxxxxxx         "
        "xxxxxxxx           "
        "xxxxxxxx           "
        "xx  xxxxx          "
        "    xxxxx          "
        "     xxxxx         "
        "     xxxxx         "
        "      xxx          ";


//转换像素数据格式 要不显示颜色异常
//XImage像素顺序RGBA --> 转frameBuffer像素顺序BGRA
void copyImage(const XImage* image, char* buffer, int width, int height, int stride) 
{
    
    
    if((image == NULL) || (buffer == NULL))
    {
    
    
        return;
    }
    char* src = (char*) image->data;
 
    for(int index=0; index< width*height; ++index)
    {
    
    
	char single_pixels[4];
	memcpy(single_pixels,src+index*4, 4);
	single_pixels[2]= (src+index*4)[0];
	single_pixels[0]= (src+index*4)[2];
	memcpy(buffer + index * 4, single_pixels, 4); 

    }	
}

//X11 默认抓取的桌面内容不带鼠标的光标, 需要单独绘制鼠标光标
void paint_mouse_pointer(XImage *image, Display* display, int x_off, int y_off, unsigned int width, unsigned int height)
{
    
    
    Display *dpy = display;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
    int pixstride = image->bits_per_pixel >> 3;

    uint8_t *pix = (uint8_t*)image->data;

    /* Code doesn't currently support 16-bit or PAL8 */
    if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
        return;

    xcim = XFixesGetCursorImage(dpy);

    x = xcim->x - xcim->xhot;
    y = xcim->y - xcim->yhot;

    to_line = min((y + xcim->height), (int)(height + y_off));
    to_column = min((x + xcim->width), (int)(width + x_off));

    for (line = max(y, y_off); line < to_line; line++) 
    {
    
    
        for (column = max(x, x_off); column < to_column; column++) 
        {
    
    
            int  xcim_addr = (line - y) * xcim->width + column - x;
            int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
            int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
            int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
            int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
            int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);

            if (a == 255) 
            {
    
    
                pix[image_addr+0] = r;
                pix[image_addr+1] = g;
                pix[image_addr+2] = b;
            } else if (a) {
    
    
                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
                pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
                pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
                pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
            }
        }
    }
    XFree(xcim);
    xcim = NULL;
}


//生成带鼠标光标的桌面截图
XImage* generateDesktopImageWithCursor(Display* display, Window root, int x, int y, unsigned int width, unsigned int height) 
{
    
    
     XImage* image = XGetImage(display, root, x, y, width, height, AllPlanes, ZPixmap);
     paint_mouse_pointer(image,display,x,y,width,height);
     return image;
}

int main(int argc, char** argv) 
{
    
    
    //开启桌面连接
    Display* disp = XOpenDisplay(NULL);
    if (!disp) 
    {
    
    
        printf("open x11 display error\n");
        exit(1);
    }
    //获取桌面窗口
    Window root = DefaultRootWindow(disp);
    XWindowAttributes attrs;
    XGetWindowAttributes(disp, root, &attrs);

    //分配每一帧的内存空间
    char* buffer = (char*) malloc(attrs.width * attrs.height * 4); // RGBA 格式
    if (!buffer) {
    
    
        printf("malloc buffer error \n");
        exit(1);
    }

    //使用 libvncserver 创建服务器
    rfbScreenInfoPtr server = rfbGetScreen(&argc, argv, attrs.width, attrs.height, 8, 3, 4);
    server->desktopName = "share desktop server ";
    server->frameBuffer = (char*) buffer;
    server->alwaysShared = true;

    //绘制客户端移动的光标
    if(!myCursor) 
    {
    
    
        myCursor = rfbMakeXCursor( 19, 19, (char*) cur, (char*) mask);
    }

    server->cursor = myCursor;	

    //初始化服务端
    rfbInitServer(server);

    while (true) 
    {
    
    
        //每100ms刷新一帧画面内容
        XImage* image = generateDesktopImageWithCursor(disp, root, 0, 0, attrs.width, attrs.height);
        copyImage(image, buffer, attrs.width, attrs.height, server->paddedWidthInBytes);
        rfbMarkRectAsModified(server, 0, 0, server->width, server->height);
        XDestroyImage(image);
        rfbProcessEvents(server, 100000); 
    }

    //清理缓存
    XCloseDisplay(disp);
    free(buffer);
    rfbShutdownServer(server, true);
    return 0;
}

Starten Sie den oben beschriebenen VNC-Server auf einem Computer und verwenden Sie dann den VNC-Client (hier wird VNC-Viewer empfohlen), um eine Verbindung zur IP des entsprechenden Computers auf einem anderen Computer herzustellen, und Sie können den Desktop auf dem anderen Computer sehen. Bisher haben wir die unidirektionale Bildschirmübertragung vom Server zum Client realisiert. Im nächsten Schritt müssen wir die vom Client gesendeten Anweisungen empfangen und die entsprechenden Maus- und Tastaturoperationen simulieren.

X11 simuliert Maus- und Tastaturbedienungen

Nachdem Sie die vom Client unter Linux gesendeten Anweisungen erhalten haben, müssen Sie die entsprechenden Maus- und Tastaturoperationen simulieren. Im Folgenden wird beschrieben, wie Sie die X11-Bibliothek zum Simulieren von Maus- und Tastaturoperationen verwenden:

Wenn im System keine X11-Entwicklungsbibliothek vorhanden ist, können Sie diese mit dem folgenden Befehl installieren:

sudo apt-get install libx11-dev libxext-dev libxtst-dev libxrender-dev libxmu-dev libxmuu-dev

//引用X11对应的库
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XTest.h> 
#include <unistd.h>         
#include <termios.h>  

//获取鼠标的位置
bool GetMousePos(int& x, int& y)
{
    
    
	Display *dpy;
	Window root;
	Window ret_root;
	Window ret_child;
	int root_x;
	int root_y;
	int win_x;
	int win_y;
	unsigned int mask;
	dpy = XOpenDisplay(NULL);
	root = XDefaultRootWindow(dpy);
	if(XQueryPointer(dpy, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask))
    {
    
    
		x = root_x;
		y = root_y;
		return true;
	}
	return false;
}

//设置鼠标的位置
bool SetMousePos(const int& x, const int& y){
    
    
	Display *dpy = XOpenDisplay(0);
	Window root = XRootWindow(dpy, 0);
	XWarpPointer(dpy, None, root, 0, 0, 0, 0, x, y);
	XFlush(dpy); 
    XCloseDisplay(dpy);
	return true;
}

//模拟鼠标左键按下
bool LeftPress(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 1, true, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟鼠标左键抬起
bool LeftRelease(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 1, false, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟鼠标右键按下
bool RightPress(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 3, true, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟鼠标右键抬起
bool RightRelease(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 3, false, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟鼠标中键按下
bool MiddlePress(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 2, true, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟鼠标中键抬起
bool MiddleRelease(){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeButtonEvent(display, 2, false, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟其它键按下
bool PressKey(int key){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeKeyEvent(display, XKeysymToKeycode(display, key), true, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//模拟其它键抬起
bool ReleaseKey(int key){
    
    
	Display *display = XOpenDisplay(NULL);
	XTestFakeKeyEvent(display, XKeysymToKeycode(display, key), false, 0);
	XFlush(display);
	XCloseDisplay(display);
	return true;
}

//获取桌面宽度
bool GetScreenWidth(int& w){
    
    
	Display* d = XOpenDisplay(NULL);
	Screen* s = DefaultScreenOfDisplay(d);
	w = s->width;
	return true;
}

//获取桌面高度
bool GetScreenHeight(int& h){
    
    
	Display* d = XOpenDisplay(NULL);
	Screen* s = DefaultScreenOfDisplay(d);
	h = s->height;
	return true;
}

//判断是否有组合键按下
bool KeyIsDown(int& key){
    
    
	XkbStateRec r;
    Display* d = XOpenDisplay(NULL);
    XkbGetState(d, XkbUseCoreKbd, &r);
	if((r.mods & 0x01) && key == 16) //Shift
		return true;
	if((r.mods & 0x04) && key == 17) //Ctrl
		return true;
	if((r.mods & 0x08) && key == 18) //Alt
		return true;
    XCloseDisplay(d);
	return false;
}

Behandeln Sie Maus- und Tastaturmeldungen in libvncserver

Nachdem wir die Funktionen zum Simulieren von Maus- und Tastaturoperationen definiert haben, können wir Maus- und Tastaturnachrichten in VNCServer empfangen und dann Operationen simulieren. Der entsprechende Verarbeitungsablauf ist wie folgt:


//鼠标消息处理
void mouseevent(int buttonMask, int x, int y, rfbClientPtr cl) 
{
    
    	
	static int oldButtonMask = 0;
	SetMousePos(x, y);

	if(buttonMask && !oldButtonMask)
	{
    
    
		if(buttonMask == 1)
			LeftPress();
		if(buttonMask == 2)
			MiddlePress();
		if(buttonMask == 4)
			RightPress();
		if(buttonMask == 8)
			WheelUp();
		if(buttonMask == 16)
			WheelDown();
	}

	if(!buttonMask && oldButtonMask){
    
    
		if(oldButtonMask == 1)
			LeftRelease();
		if(oldButtonMask == 2)
			MiddleRelease();
		if(oldButtonMask == 4)
			RightRelease();
	}
	oldButtonMask = buttonMask;
}

//处理按键消息
void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl) 
{
    
    
	if(down){
    
    
		PressKey(key);
	}
	else {
    
    
		ReleaseKey(key);
	}
}


int main(int argc, char** argv) 
{
    
    
    //省略重复代码
    //使用 libvncserver 创建服务器
    rfbScreenInfoPtr server = rfbGetScreen(&argc, argv, attrs.width, attrs.height, 8, 3, 4);
    server->desktopName = "share desktop server ";
    server->frameBuffer = (char*) buffer;
    server->alwaysShared = true;
    server->alwaysShared = true;

    //注册鼠标键盘消息的回调函数
	server->ptrAddEvent = mouseevent;
	server->kbdAddEvent = keyevent;

    //省略重复代码
    return 0;
}

Durch die obige Verarbeitung können wir einen einfachen VNC-Server erstellen, der grundlegende Fernsteuerungsbefehle ausführen kann. Wenn Sie eine weitere tiefgreifende Optimierung wünschen, können Sie auf andere VNC-Open-Source-Projekte verweisen, wie zum Beispiel: TigerVNC, UltraVNC, TightVNC, RealVNC

Ich denke du magst

Origin blog.csdn.net/yang1fei2/article/details/132371918
Empfohlen
Rangfolge