So, let’s begin!
Enter the following commands in your terminal:
Now you should have folder called “js-vnc” with file named “package.json” in it. Use the following line of code to install almost all the dependencies:
Unfortunately node-png is not in the npm registry and we have to build it from source. Use:
and that was all…Now we can start creating our server. Let’s include all the required modules and initialize an array which will contains all the connected clients. Create a file called “server.js” in your project base directory and add the following content in it:
and now let’s create a static directory for our static resources:
now we can set the static directory of the connect server:
Now you can execute the following lines of code:
Now open: http://localhost:8091/index.html. If everything went well you should see the text: “Hello, world!”.
So far, so good! Now lets create a socket.io server. We want the client to connect to our proxy server and exchange data through socket.io with it. When the client is connected (i.e. the connection is established) it sends “init” message which contains the data required for the proxy server to connect to the VNC server – host, port and password. So let’s handle these events:
We already listen for incoming connections with socket.io on port 8091 so now we just subscribe to the “connection” event. When the client connects to our socket.io server we subscribe the server to the “init” event. It’s the message I told about earlier. Its parameters will contains data required for the proxy server to connect to the VNC server. In the callback associated with the “init” event we first create new RFB (remote framebuffer) connection. This method will return a RFB handler, we can use it to exchange data with the VNC server. After initialising the RFB connection we subscribe to three more events: “mouse”, “keyboard” and “disconnect”. The mouse event has parameter containing the position of the cursor and button state (1 for mouse down 0 for mouse up). For this demo we will support only the first mouse button. The keyboard event accepts as parameters the key code of the button which have triggered the event and flag which indicates whether the button is down or up. When the disconnect event happens we disconnect from the VNC server and release all the data for the current connection.
Here is posted the function createRfbConnection:
In the method “addEventHandlers” we add event handlers to the RFB handler we received from the “createConnection” method:
When the RFB connection is established we send “init” event to the client (our browser) with the size of the screen. For simplicity we won’t scale the screen in the browser. After sending the “init” event to the browser we add the client to our connected clients and subscribe to the event “rect”. The “rect” event will trigger when there is a screen update. For extra simplicity we will use raw encoding.
The last method from our Node.js proxy we will look at is the “handleFrame” method.
We create new binary buffer with size “rect.width * rect.height * 3” and process the received pixmap. After that we create new PNG image from it by specifying its width, height and format (rgb). We encode the image and emit the event “frame” with parameters the coordinates of the image, its size and the actual image in base64 format.
And we’re done with our server! There’s one detail which I missed with purpose. In this version we don’t use all the advantages of the RFB protocol. It has incremental frame transmitting which I’ve omitted. RFB implementation with excluded incremental transmission can be downloaded from here.
And now our simple client!
Edit the content of the file ./static/index.html with the following content:
We’ve added simple bootstrap form and a canvas. Now create sub-directory of our project base directory: “./static/js” and create the file “Client.js” in it.
We will wrap the whole code in an IIFE to omit any globals. Let’s create a simple object which will hold our settings:
Now lets add some code for initialisation after the “Config” declaration:
We simply add event handler on click of the button with id “loginBtn”. In the callback we initialise new “Screen” object with our canvas as argument of the constructor function and create new “Client” with the Screen object as parameter. We call the connect method of the client with the specified parameters - host, port, password and a callback which will be invoked when the client is being connected.
This is the definition of the connect method:
It creates new socket.io connection with the server’s socket.io server host name and port and emits the “init” event we already talked about (contains data required for connection to the VNC server). After that we add handlers which will listen for events coming from the server - the “init” and “rect” event. Since the “rect” event is a bit more interesting we will look in its handling:
As you see we simply delegate the drawing to the screen and pass it the frame as argument. And here is the actual drawing on the canvas:
We create new image with the frame’s width and height as dimensions and we set image’s src to be equal to the frame’s base64 image. After that, when the image is loaded we draw it onto the canvas.
The last interesting moment is the handling of the mouse and keyboard events. Of course we delegate it to the screen object:
As you see we add the mouse and keyboard event handlers and in the event callbacks we simply emit new socket.io events, respectively: “mouse” and “keyboard”, with their arguments. For simplicity our canvas is absolutely positioned and has coordinates (0, 0).
Quick video demo