En informatique, Virtual Network Computing (VNC) est un système de partage de bureau graphique qui utilise le protocole Remote Framebuffer (RFB) pour contrôler à distance un autre ordinateur. Il transmet les événements du clavier et de la souris d'un ordinateur à un autre et relaie les mises à jour de l'écran graphique dans l'autre sens sur un réseau.
RFB est un protocole simple d'accès à distance aux interfaces utilisateur graphiques. Comme il fonctionne au niveau du tampon de trame, il est applicable à tous les systèmes et applications de fenêtrage, y compris Microsoft Windows, Mac OS X et X Window System.
Dans cet article, je vais montrer comment implémenter le protocole côté serveur RFB et démontrer avec une petite application Java Swing comment transmettre la fenêtre principale via une connexion TCP aux visionneuses VNC. L'idée est de démontrer les fonctionnalités de base du protocole et l'implémentation possible en Java.
Le lecteur doit avoir une connaissance de base du langage de programmation Java et doit être familiarisé avec les concepts de base de la mise en réseau TCP / IP, du modèle client-serveur, etc. Idéalement, le lecteur est un Développeur Java et possède une certaine expérience des implémentations VNC bien connues telles que RealVNC, UltraVNC, TightVNC, etc.
La spécification du protocole RFB est jolie bien défini . Selon Wikipedia, le protocole RFB a plusieurs versions. Pour cet article, nous nous concentrerons sur les messages courants qui doivent être correctement compris par la plupart des implémentations VNC, quelle que soit la version du protocole.
Après qu'un visualiseur VNC (client) a établi une connexion TCP à un serveur VNC (service RFB), la première phase implique l'échange de la version du protocole:
RFB Service ----------- 'RFB 003.003
' -------> VNC viewer RFB Service <---------- 'RFB 003.008
' -------- VNC viewer
C'est un simple flux d'octets qui peut être décodé en Caractères ASCII , comme «RFB 003.008 n».
Une fois que cela est fait, l'étape suivante est l'authentification. Le serveur VNC envoie un tableau d'octets pour indiquer le type d'authentification qu'il prend en charge. Par exemple:
RFB Service ----------- 0x01 0x02 -----------> VNC viewer RFB Service <----------- 0x02 ----------- VNC viewer
Ici, le serveur VNC n'a envoyé qu'un seul type d'authentification possible (0x02). Le premier octet 0x01 indique le nombre de types d'authentification disponibles. Le visualiseur VNC doit répondre avec la valeur 0x02, car c'est le seul type possible pris en charge par le serveur dans cet exemple.
Ensuite, le serveur enverra un défi d'authentification (selon l'algorithme, il y en a plusieurs), et le client doit répondre avec un message de réponse de défi approprié et attendre que le serveur confirme la réponse. Une fois le client authentifié, il peut poursuivre le processus d'établissement de session.
Le moyen le plus simple ici est de ne choisir aucune authentification. Le protocole RFB n'est de toute façon pas sécurisé, quel que soit le mécanisme d'authentification. Si la sécurité est importante, la bonne façon serait de tunneliser les sessions RFB via des connexions VPN ou SSH.
À ce stade, la visionneuse VNC envoie un message de bureau partagé qui indique si le client partagera et autorisera d'autres visionneuses VNC à se connecter au même bureau. C'est à la mise en œuvre du service RFB de prendre en compte ce message et éventuellement d'empêcher plusieurs téléspectateurs VNC de partager le même écran. Ce message ne mesure que 1 octet et une valeur valide est 0x00 ou 0x01.
Enfin, le serveur RFB envoie un message d'initialisation du serveur, qui contient la dimension de l'écran, les bits par pixel, la profondeur, le drapeau big endian et les drapeaux de couleurs vraies, les valeurs maximales pour les couleurs rouge, verte et bleue, les positions des bits en pixel pour les couleurs rouge, vert et bleu , et chaîne / titre du bureau. Les deux premiers octets représentent la largeur de l'écran en pixels, les deux octets suivants sont la hauteur de l'écran. Après les octets de hauteur d'écran, les bits par octet de pixel doivent être présents dans le message. La valeur est généralement 8, 16 ou 32. Sur la plupart des systèmes modernes avec une plage de couleurs complète, les bits par octet de pixel ont la valeur 32 (0x20). Il indique au client qu'il peut demander la couleur complète pour chaque pixel du serveur. L'octet big endian est différent de zéro uniquement si les pixels sont dans l'ordre big endian. Si l'octet de couleur vrai est différent de zéro (vrai), les six octets suivants spécifient comment extraire les intensités de couleur rouge, verte et bleue de la valeur du pixel. Les six octets suivants sont les valeurs maximales autorisées pour la composante rouge, verte et bleue du pixel. Ceci est important en mode couleur 8 bits, où seuls quelques bits sont disponibles pour chaque composant de couleur. Les décalages rouge, vert et bleu déterminent les positions des bits pour chaque couleur. Les trois derniers octets sont du remplissage et doivent être ignorés par le client. Après le format de pixel, il y a un octet qui définit la longueur d'une chaîne pour le titre du bureau. Le titre du bureau est une chaîne codée ASCII dans un tableau d'octets de longueur arbitraire.
Après le message d'initialisation du serveur, le service RFB doit lire les messages client à partir du socket et les décoder. Il existe 6 types de messages:
La documentation du protocole est assez exacte et explique chaque message. Pour chaque message, chaque octet est expliqué. Par exemple, message d'initialisation du serveur:
No d'octets | Type | La description |
---|---|---|
2 | U16 | largeur du tampon |
2 | U16 | hauteur du tampon |
16 | PIXEL_FORMAT | format-pixel-serveur |
4 | U32 | nom-longueur |
nom-longueur | Réseau U8 | chaîne de nom |
Ici, PIXEL_FORMAT c'est:
No d'octets | Type | La description |
---|---|---|
un | U8 | bits par pixel |
un | U8 | profondeur |
un | U8 | drapeau-big-endian |
un | U8 | drapeau de couleur vraie |
2 | U16 | rouge-max |
2 | U16 | green-max |
2 | U16 | bleu-max |
un | U8 | décalage vers le rouge |
un | U8 | virage vert |
un | U8 | décalage vers le bleu |
3 | rembourrage |
U16 signifie un entier 16 bits non signé (deux octets), U32 est un entier 32 bits non signé, un tableau U8 est un tableau d'octets, etc.
qu'est-ce qu'un architecte de solutions certifié aws
Une application serveur Java typique se compose d'un thread écoutant les connexions client et de plusieurs threads gérant les connexions client.
/* * Use TCP port 5902 (display :2) as an example to listen. */ int port = 5902; ServerSocket serverSocket; serverSocket = new ServerSocket(port); /* * Limit sessions to 100. This is lazy way, if * somebody really open 100 sessions, server socket * will stop listening and no new VNC viewers will be * able to connect. */ while (rfbClientList.size() <100) { /* * Wait and accept new client. */ Socket client = serverSocket.accept(); /* * Create new object for each client. */ RFBService rfbService = new RFBService(client); /* * Add it to list. */ rfbClientList.add(rfbService); /* * Handle new client session in separate thread. */ (new Thread(rfbService, 'RFBService' + rfbClientList.size())).start(); }
Ici, le port TCP 5902 a été choisi (affichage: 2) et la boucle while attend qu'un client se connecte. Méthode ServerSocket.accept () bloque et fait attendre le thread pour une nouvelle connexion client. Une fois le client connecté, un nouveau thread RFBService est créé qui gère les messages du protocole RFB reçus du client.
La classe RFBService implémente l'interface Runnable. Il est plein de méthodes pour lire les octets depuis le socket. Méthode courir() est important, qui est exécuté immédiatement lorsque le thread est lancé à la fin de la boucle:
@Override public void run() { try { /* * RFB server has to send protocol version string first. * And wait for VNC viewer to replay with * protocol version string. */ sendProtocolVersion(); String protocolVer = readProtocolVersion(); if (!protocolVer.startsWith('RFB')) { throw new IOException(); }
Ici méthode sendProtocolVersion () envoie une chaîne RFB au client (visionneuse VNC), puis lit la chaîne de version de protocole à partir du client. Le client doit répondre par quelque chose comme «RFB 003.008 n». Méthode readProtocolVersion () est bien sûr bloquante, comme toute méthode dont le nom commence par le mot read.
private String readProtocolVersion() throws IOException { byte[] buffer = readU8Array(12); return new String(buffer); }
La méthode readProtocolVersion () est simple: elle lit 12 octets à partir du socket et renvoie une valeur de chaîne. La fonction readU8Array (int) lit le nombre spécifié d'octets, dans ce cas 12 octets. S'il n'y a pas assez d'octets à lire sur le socket, il attend:
private byte[] readU8Array(int len) throws IOException { byte[] buffer = new byte[len]; int offset = 0, left = buffer.length; while (offset Semblable à readU8Array (entier) , méthodes readU16int () et readU32int () existent qui lisent les octets de la socket et renvoient une valeur entière.
Après avoir envoyé la version du protocole et lu la réponse, le service RFB doit envoyer un message de sécurité:
/* * RFB server sends security type bytes that may request * a user to type password. * In this implementation, this is set to simples * possible option: no authentication at all. */ sendSecurityType();
Dans cette implémentation, le moyen le plus simple est choisi: ne nécessite aucun mot de passe du côté client VNC.
private void sendSecurityType() throws IOException { out.write(SECURITY_TYPE); out.flush(); }
où SECURITY_TYPE est un tableau d'octets:
private final byte[] SECURITY_TYPE = {0x00, 0x00, 0x00, 0x01};
Ce tableau d'octets de la version 3.3 du protocole RFB signifie que le visualiseur VNC n'a pas besoin d'envoyer de mot de passe.
Ensuite, ce que le service RFB doit obtenir du client est l'indicateur de bureau partagé. C’est un octet sur le socket.
/* * RFB server reads shared desktop flag. It's a single * byte that tells RFB server * should it support multiple VNC viewers connected at * same time or not. */ byte sharedDesktop = readSharedDesktop();
Une fois que l'indicateur de bureau partagé est lu à partir du socket, nous l'ignorons dans notre implémentation.
Le service RFB doit envoyer un message d'initialisation du serveur:
/* * RFB server sends ServerInit message that includes * screen resolution, * number of colors, depth, screen title, etc. */ screenWidth = JFrameMainWindow.jFrameMainWindow.getWidth(); screenHeight = JFrameMainWindow.jFrameMainWindow.getHeight(); String windowTitle = JFrameMainWindow.jFrameMainWindow.getTitle(); sendServerInit(screenWidth, screenHeight, windowTitle);
La classe JFrameMainWindow est JFrame, qui est ici à des fins de démonstration en tant que source de graphiques. Le message d'initialisation du serveur a une largeur et une hauteur d'écran obligatoires en pixels et un titre de bureau. Dans cet exemple, il s'agit du titre de JFrame obtenu par la méthode getTitle ().
Après le message d'initialisation du serveur, le thread de service RFB effectue une boucle en lisant à partir du socket six types de messages:
/* * Main loop where clients messages are read from socket. */ while (true) { /* * Mark first byte and read it. */ in.mark(1); int messageType = in.read(); if (messageType == -1) { break; } /* * Go one byte back. */ in.reset(); /* * Depending on message type, read complete message on socket. */ if (messageType == 0) { /* * Set Pixel Format */ readSetPixelFormat(); } else if (messageType == 2) { /* * Set Encodings */ readSetEncoding(); } else if (messageType == 3) { /* * Frame Buffer Update Request */ readFrameBufferUpdateRequest(); } else if (messageType == 4) { /* * Key Event */ readKeyEvent(); } else if (messageType == 5) { /* * Pointer Event */ readPointerEvent(); } else if (messageType == 6) { /* * Client Cut Text */ readClientCutText(); } else { err('Unknown message type. Received message type = ' + messageType); } }
Chaque méthode readSetPixelFormat () , readSetEncoding () , readFrameBufferUpdateRequest () , ... readClientCutText () bloque et déclenche une action.
Par exemple, readClientCutText () La méthode lit le texte qui est encodé dans le message lorsque l'utilisateur coupe le texte côté client, puis le visualiseur VNC envoie le texte via le protocole RFB au serveur. Le texte est ensuite placé côté serveur dans le Presse-papiers.
Client Messages
Les six messages doivent être pris en charge par le service RFB, au moins au niveau des octets: lorsque le client envoie un message, une longueur d'octet complète doit être lue. Cela est dû au fait que le protocole RFB est orienté octet et qu'il n'y a pas de frontière entre deux messages.
Le message le plus importé est la demande de mise à jour du tampon de trame. Le client peut demander une mise à jour complète ou une mise à jour incrémentielle de l'écran.
private void readFrameBufferUpdateRequest() throws IOException { int messageType = in.read(); int incremental = in.read(); if (messageType == 0x03) { int x_pos = readU16int(); int y_pos = readU16int(); int width = readU16int(); int height = readU16int(); screenWidth = width; screenHeight = height; if (incremental == 0x00) { incrementalFrameBufferUpdate = false; int x = JFrameMainWindow.jFrameMainWindow.getX(); int y = JFrameMainWindow.jFrameMainWindow.getY(); RobotScreen.robo.getScreenshot(x, y, width, height); sendFrameBufferUpdate(x_pos, y_pos, width, height, 0, RobotScreen.robo.getColorImageBuffer()); } else if (incremental == 0x01) { incrementalFrameBufferUpdate = true; } else { throw new IOException(); } } else { throw new IOException(); } }
Le premier octet du message de demande de tampon de trame est le type de message. La valeur est toujours 0x03. L'octet suivant est un indicateur incrémentiel, qui indique au serveur d'envoyer une image complète ou simplement une différence. En cas de demande de mise à jour complète, le service RFB prendra une capture d'écran de la fenêtre principale à l'aide de la classe RobotScreen et l'enverra au client.
S'il s'agit d'une demande incrémentielle, un indicateur incrementalFrameBufferUpdate sera défini sur true. Cet indicateur sera utilisé par les composants Swing pour vérifier s'ils doivent envoyer des parties de l'écran qui ont changé. Habituellement, JMenu, JMenuItem, JTextArea, etc. doivent effectuer une mise à jour incrémentielle de l'écran lorsque l'utilisateur déplace le pointeur de la souris, clique, envoie une frappe, etc.
La méthode sendFrameBufferUpdate (int, int, int, int, int []) vide le tampon d'image sur le socket.
public void sendFrameBufferUpdate(int x, int y, int width, int height, int encodingType, int[] screen) throws IOException { if (x + width > screenWidth || y + height > screenHeight) { err ('Invalid frame update size:'); err (' x = ' + x + ', y = ' + y); err (' width = ' + width + ', height = ' + height); return; } byte messageType = 0x00; byte padding = 0x00; out.write(messageType); out.write(padding); int numberOfRectangles = 1; writeU16int(numberOfRectangles); writeU16int(x); writeU16int(y); writeU16int(width); writeU16int(height); writeS32int(encodingType); for (int rgbValue : screen) { int red = (rgbValue & 0x000000FF); int green = (rgbValue & 0x0000FF00) >> 8; int blue = (rgbValue & 0x00FF0000) >> 16; if (bits_per_pixel == 8) { out.write((byte) colorMap.get8bitPixelValue(red, green, blue)); } else { out.write(red); out.write(green); out.write(blue); out.write(0); } } out.flush(); }
La méthode vérifie que les coordonnées (x, y) ne sortent pas de l'écran avec la largeur x la hauteur du tampon d'image. La valeur du type de message pour la mise à jour du tampon de trame est 0x00. La valeur de remplissage est généralement 0x00 et doit être ignorée par la visionneuse VNC. Le nombre de rectangles est une valeur de deux octets et définit le nombre de rectangles qui suivent dans le message.
dans quelle langue est écrit os x
Chaque rectangle a une coordonnée, une largeur et une hauteur en haut à gauche, un type de codage et des données de pixels. Certains formats d'encodage efficaces peuvent être utilisés, tels que zrle, hextile et tight. Cependant, pour garder les choses simples et faciles à comprendre, nous utiliserons le codage brut dans notre implémentation.
Le codage brut signifie que la couleur des pixels est transmise en tant que composant RVB. Si le client a défini le codage des pixels sur 32 bits, 4 octets sont transmis pour chaque pixel. Si le client utilise le mode couleur 8 bits, chaque pixel est transmis sous la forme d'un octet. Le code est affiché en boucle for. Notez que pour le mode 8 bits, la carte des couleurs est utilisée pour trouver la meilleure correspondance pour chaque pixel de la capture d'écran / du tampon d'image. Pour le mode pixel 32 bits, le tampon d'image contient un tableau d'entiers, chaque valeur a des composants RVB multiplexés.
Application de démonstration Swing
L'application de démonstration Swing contient un écouteur d'action qui se déclenche sendFrameBufferUpdate (int, int, int, int, int []) méthode. Habituellement, les éléments d'application, comme les composants Swing, doivent avoir des écouteurs et envoyer le changement d'écran au client. Par exemple, lorsque l'utilisateur tape quelque chose dans JTextArea, il doit être transmis au visualiseur VNC.
public void actionPerformed(ActionEvent arg0) { /* * Get dimensions and location of main JFrame window. */ int offsetX = JFrameMainWindow.jFrameMainWindow.getX(); int offsetY = JFrameMainWindow.jFrameMainWindow.getY(); int width = JFrameMainWindow.jFrameMainWindow.getWidth(); int height = JFrameMainWindow.jFrameMainWindow.getHeight(); /* * Do not update screen if main window dimension has changed. * Upon main window resize, another action listener will * take action. */ int screenWidth = RFBDemo.rfbClientList.get(0).screenWidth; int screenHeight = RFBDemo.rfbClientList.get(0).screenHeight; if (width != screenWidth || height != screenHeight) { return; } /* * Capture new screenshot into image buffer. */ RobotScreen.robo.getScreenshot(offsetX, offsetY, width, height); int[] delta = RobotScreen.robo.getDeltaImageBuffer(); if (delta == null) { offsetX = 0; offsetY = 0; Iterator it = RFBDemo.rfbClientList.iterator(); while (it.hasNext()) { RFBService rfbClient = it.next(); if (rfbClient.incrementalFrameBufferUpdate) { try { /* * Send complete window. */ rfbClient.sendFrameBufferUpdate( offsetX, offsetY, width, height, 0, RobotScreen.robo.getColorImageBuffer()); } catch (SocketException ex) { it.remove(); } catch (IOException ex) { ex.printStackTrace(); it.remove(); } rfbClient.incrementalFrameBufferUpdate = false; } } } else { offsetX = RobotScreen.robo.getDeltaX(); offsetY = RobotScreen.robo.getDeltaY(); width = RobotScreen.robo.getDeltaWidth(); height = RobotScreen.robo.getDeltaHeight(); Iterator it = RFBDemo.rfbClientList.iterator(); while (it.hasNext()) { RFBService rfbClient = it.next(); if (rfbClient.incrementalFrameBufferUpdate) { try { /* * Send only delta rectangle. */ rfbClient.sendFrameBufferUpdate( offsetX, offsetY, width, height, 0, delta); } catch (SocketException ex) { it.remove(); } catch (IOException ex) { ex.printStackTrace(); it.remove(); } rfbClient.incrementalFrameBufferUpdate = false; } } } }
Le code de cet écouteur d'action est assez simple: il prend une capture d'écran de la fenêtre principale JFrameMain en utilisant la classe RobotScreen, puis il est déterminé si une mise à jour partielle de l'écran est nécessaire. Variable diffUpdateOfScreen est utilisé comme indicateur pour une mise à jour partielle. Et enfin, un tampon d'image complet ou seules des lignes différentes sont transmises au client. Ce code considère également plus de clients connectés, c'est pourquoi l'itérateur est utilisé et la liste des clients est maintenue dans RFBDemo.rfbClientList membre.
L'écouteur d'action de mise à jour de Framebuffer peut être utilisé dans Timer qui peut être démarré par n'importe quel changement de JComponent:
/* * Define timer for frame buffer update with 400 ms delay and * no repeat. */ timerUpdateFrameBuffer = new Timer(400, new ActionListenerFrameBufferUpdate()); timerUpdateFrameBuffer.setRepeats(false);
Ce code est dans le constructeur de la classe JFrameMainWindow. Le minuteur est démarré dans la méthode doIncrementalFrameBufferUpdate ():
public void doIncrementalFrameBufferUpdate() { if (RFBDemo.rfbClientList.size() == 0) { return; } if (!timerUpdateFrameBuffer.isRunning()) { timerUpdateFrameBuffer.start(); } }
Les autres écouteurs d'action appellent généralement la méthode doIncrementalFrameBufferUpdate ():
public class DocumentListenerChange implements DocumentListener { @Override public void changedUpdate(DocumentEvent e) { JFrameMainWindow jFrameMainWindow = JFrameMainWindow.jFrameMainWindow; jFrameMainWindow.doIncrementalFrameBufferUpdate(); } // ... }
Cette méthode doit être simple et facile à suivre. Seule une référence à l'instance JFrameMainWindow est nécessaire et un seul appel de doIncrementalFrameBufferUpdate () méthode. La méthode vérifiera s'il y a des clients connectés, et s'il y en a, une minuterie timerUpdateFrameBuffer sera lancé. Une fois le chronomètre démarré, l'écouteur d'action prendra en fait une capture d'écran et sendFrameBufferUpdate () est exécuté.

La figure ci-dessus montre la relation de l'auditeur avec la procédure de mise à jour du tampon de trame. La plupart des écouteurs sont déclenchés lorsque l'utilisateur effectue une action: clique, sélectionne du texte, tape quelque chose dans la zone de texte, etc. Puis fonction membre doIncrementalFramebufferUpdate () est exécuté ce qui démarre le chronomètre timerUpdateFrameBuffer . La minuterie finira par appeler sendFrameBufferUpdate () dans la classe RFBService et cela provoquera une mise à jour de l'écran côté client (visionneuse VNC).
c corp contre s corp contre llc
Capturez l'écran, jouez les frappes et déplacez le pointeur de la souris sur l'écran
Java a une classe Robot intégrée qui permet au développeur d'écrire une application qui saisira des captures d'écran, enverra des clés, manipulera le pointeur de la souris, produira des clics, etc.
Pour saisir la zone de l'écran où la fenêtre JFrame est affichée, RobotScreen est utilisé. La méthode principale est getScreenshot (int, int, int, int) qui capture une région de l'écran. Les valeurs RVB de chaque pixel sont stockées dans un tableau int []:
public void getScreenshot(int x, int y, int width, int height) { Rectangle screenRect = new Rectangle(x, y, width, height); BufferedImage colorImage = robot.createScreenCapture(screenRect); previousImageBuffer = colorImageBuffer; colorImageBuffer = ((DataBufferInt) colorImage.getRaster().getDataBuffer()).getData(); if (previousImageBuffer == null || previousImageBuffer.length != colorImageBuffer.length) { previousImageBuffer = colorImageBuffer; } this.width = width; this.height = height; }
La méthode stocke les pixels dans le tableau colorImageBuffer. Pour obtenir des données de pixels, getColorImageBuffer () méthode peut être utilisée.
La méthode enregistre également le tampon d'image précédent. Il est possible de ne récupérer que les pixels qui ont été modifiés. Pour obtenir uniquement la différence de zone d'image, utilisez la méthode getDeltaImageBuffer () .
L'envoi de frappes au système est facile avec la classe Robot. Cependant, certains codes de clé spéciaux reçus des téléspectateurs VNC doivent d'abord être traduits correctement. La classe RobotKeyboard a une méthode sendKey (int, int) qui gère les touches spéciales et les touches alphanumériques:
public void sendKey(int keyCode, int state) { switch (keyCode) { case 0xff08: doType(VK_BACK_SPACE, state); break; case 0xff09: doType(VK_TAB, state); break; case 0xff0d: case 0xff8d: doType(VK_ENTER, state); break; case 0xff1b: doType(VK_ESCAPE, state); break; … case 0xffe1: case 0xffe2: doType(VK_SHIFT, state); break; case 0xffe3: case 0xffe4: doType(VK_CONTROL, state); break; case 0xffe9: case 0xffea: doType(VK_ALT, state); break; default: /* * Translation of a..z keys. */ if (keyCode >= 97 && keyCode <= 122) { /* * Turn lower-case a..z key codes into upper-case A..Z key codes. */ keyCode = keyCode - 32; } doType(keyCode, state); } }
L'état de l'argument détermine si la touche est enfoncée ou relâchée. Après traduction correcte du code clé en constante VT, méthode doType (int, int) passez la valeur de la clé à Robot et l'effet est le même que l'utilisateur local a appuyé sur la touche du clavier:
private void doType(int keyCode, int state) { if (state == 0) { robot.keyRelease(keyCode); } else { robot.keyPress(keyCode); } }
Similaire à RobotKeyboard est la classe RobotMouse qui gère les événements de pointeur et fait bouger le pointeur de la souris et cliquer.
public void mouseMove(int x, int y) { robot.mouseMove(x, y); }
Les trois classes RobotScreen, RobotMouse et RobotKeyboard allouent une nouvelle instance de Robot dans le constructeur:
this.robot = new Robot();
Nous n'avons qu'une seule instance de chacun, car il n'est pas nécessaire au niveau de l'application d'avoir plus d'une instance de la classe RobotScreen, RobotMouse ou RobotKeyboard.
public static void main(String[] args) { ... /* * Initialize static Robot objects for screen, keyboard and mouse. */ RobotScreen.robo = new RobotScreen(); RobotKeyboard.robo = new RobotKeyboard(); RobotMouse.robo = new RobotMouse(); ... }
Dans cette application de démonstration, ces instances sont créées dans principale() fonction.
Le résultat est une application basée sur Swing en Java qui agit comme un fournisseur de services RFB et permet aux visualiseurs VNC standard de s'y connecter:

Conclusion
Le protocole RFB est largement utilisé et accepté. Des implémentations client sous forme de visionneuses VNC existent pour presque toutes les plates-formes et tous les appareils. Le but principal est d'afficher les bureaux à distance, mais il peut également y avoir d'autres applications. Par exemple, vous pouvez créer des outils graphiques astucieux et y accéder à distance pour améliorer votre workflows à distance .
Cet article couvre les bases du protocole RFB, le format des messages, comment envoyer une partie de l'écran et comment gérer le clavier et la souris. Le code source complet avec l'application de démonstration Swing est disponible sur GitHub .