📜 ⬆️ ⬇️

Node.js as a proxy data server via websockets

The next bike is an easy way to stop smoking to create a steady asynchronous data flow between virtually any data server and a browser.

Preamble : one of the projects that I accompany is the integrated GPS system for monitoring vehicles. It contains a server for processing and storing data from car trackers and a desktop client that draws real-time traffic on a rough raster map, which is broken into tiles with a total volume of the order of gigabytes. The project management commissioned me to create a web client based on Google-Yandex and other mimic vector maps for quick access to visual data from any place and from any device, and not just from the desktop.

The task was performed as quickly and as cost-effectively as possible : simple php scripts were written that connect to the GPS data processing server, give a request, wait for a response, and return the response to the web client. Accordingly, a simple client was laid out, which systematically, by timer, by means of the good old $ .ajax (), gave POST requests to php scripts and beautifully painted the answer to the aforementioned remarkable vector online maps.

With all the advantages, there were obvious disadvantages of such a path - synchronous requests sometimes required a lot of time to wait for a response, because asynchronous responses from the same server climbed to the status of connected trackers from the data server socket, they needed to be filtered and wait for an answer to the original request. And of course, the client-side browser sometimes started to criminally use its cache and ignore fresh data. Again, as the number of connected clients increased, the Apache began to actively consume the memory of the IPC on which it all spun. Experimentally it was calculated that the server polling interval of 20 seconds does not stress the server very much and at the same time maintains interaktivnost in the web client.
')
Such a scheme is quite satisfied with the manual - the product has successfully started, users have switched to using the web client. But the implemented solution did not suit me, as a lover of everything beautiful and harmonious.

Further attempts to write a proxy server for transferring data to a web client in Java were eventually buried due to insufficient knowledge of it and the need to deploy Tomcat or something similar on the server, which would not result in a significant performance increase.

And here Node.js and the SockJS library came to the rescue , which implements a successful emulation of an asynchronous web socket connection and does it somewhat better than socket.io, which was already written here in due time.

Looking ahead, I’ll immediately tell you why I’m writing about this - implementing the solution described below reduced the server load every thirty , works in all modern browsers (of course, I don’t mean IE6-7, although in IE8 it already works) and provides a very high speed data transmission. The solution is offered fairly universal, the same method can be used to process almost any stream of asynchronous data (site parsing, chat server, online toy, rover control system ...) and does not require in-depth programming knowledge, so it can be deployed quite quickly with any sufficiently prepared coder.

So, a server that will process information from a data server and transmit it asynchronously via a web socket connection to a web client:

Node.js
var http = require('http'), net = require('net'), sockjs = require('sockjs'), ADDR_GPS = "127.0.0.1", //   ,  - PORT_GPS = 3201, //   server = sockjs.createServer(); server.on('connection', function(conn) { //         - SockJS   //   ,      , //     ,     var com = new Commander(ADDR_GPS, PORT_GPS, conn, function(e){ console.log("! We had an Error in socket: ", e, "at ", new Date()); conn.close(); }); conn.on('data', function(data) { //         JSON //        var dat = JSON.parse(data); if(dat.command == "@auth") { //    , com.auth(dat.param.log, dat.param.pwd); } else if(dat.command == "@bye") { // -      com.bye(); conn.close(); } }); conn.on('close', function() { console.log("Websocket connection closed"); com = null; }); }); //  -HTTP-        // SockJS,    http://mydomen.com:8081/data var srv = http.createServer(); server.installHandlers(srv, {prefix:'/data'}); srv.listen(8081, '0.0.0.0'); var Commander = function (adr, port, clientConn, onError) { //        var self = this; this.status = 0; this.chunk = ""; //     this.answers = []; //      this.connection = clientConn; //    , //        this.client = new net.Socket(); //      this.client.connect(port,adr,function(){ //     console.log("New connect to created..."); }); this.client.on('data', function(data) { //         - self.onData(data); }); this.client.on('error', function(e) { //      onError(e); self.client.destroy(); }); }; Commander.prototype.auth = function(login, pass) { //            console.log("written auth for "+ login); this.client.write('(auth "'+login+'" "'+pass+'")\n'); }; Commander.prototype.bye = function() { //     this.client.write('(exit)\n'); }; Commander.prototype.onData = function(data) { //       ,      , //    -,      //    ,       //   -        var pos; this.chunk+=data.toString(); pos=this.chunk.indexOf('\n'); if(pos > -1) { this.connection.write(this.chunk.substring(0,pos)); this.chunk = this.chunk.substring(pos); } }; 



Client, requires connection module handler SockJS. Sorry for the sheet, but to spread the styles / scripts by file for an example of understanding, IMHO, is not necessary
index.html
 <!DOCTYPE html> <html lang="ru"> <head> <meta charset="utf-8"> <title>   </title> <style> body { padding:0; margin: 0; font: 10pt sans-serif, Arial, Tahoma; } h1 { font-size: 2em; margin: 0.8em 0; } h3 { font-size:1.5em; margin: 0.1em; } #content { position: relative; margin: 0 auto; width:960px; min-width:800px; } #left { position:absolute; top:0px; left:0px; padding:2px; width:220px; height:560px; } #right { position:absolute; top:0px; left:250px; padding:2px; width:710px; height:560px; } #scroller { position:relative; width: 400px; height:90%; overflow-y:auto; border:1px dotted black; padding:5px; margin-top:10px; } .off { color:red; } .on { color: green; } .inBottom { position: absolute; bottom: 20px; } </style> <script src="sockjs-0.3.4.min.js" type="text/javascript"></script> <script> var sock;stat = document.getElementById("status"); function connect() { //      sock = new SockJS('http://mysite.com:8081/data'); var l = document.getElementById("login").value, p = document.getElementById("passw").value stat = document.getElementById("status"); setTimeout(function(){ //    ,       sock.send(toJSON("@auth", { log: l, pwd: p })); },2000); sock.onopen = function() { //   ,     stat.innerHTML = "ON"; stat.className = "on"; }; sock.onmessage = function(e) { //   ,     ,   //       "data" document.getElementById("scroller").innerHTML += "<p>"+e.data+"</p>"; }; sock.onclose = function() { //    ,     stat.innerHTML = "OFF"; stat.className = "off"; }; } function disconnect() { //   if(sock !== undefined) { sock.send(toJSON("@bye", {})); } } function toJSON (com, param){ return JSON.stringify({ command: com, param: param }); } </script> </head> <body> <div id="content"> <div id="left"> <p style="width:100%;"> <input type="text" id="login" style="float:right;"></p> <p style="width:100%;"> <input type="password" id="passw" style="float:right;"></p> <button onclick="connect();"></button><button onclick="disconnect();"></button><br> <p class="inBottom">: <span id="status"></span></p> </div> <div id="right"> <div id="scroller"></div> </div> </div> </body> </html> 



I tried to provide the code with comments on all key points. I can’t give live online demos - on my server there are data from real organizations that will not be happy about such close attention to their transport. Although it is fun to watch online how the combine works - for half a day a piece of the field is hatched on the map, and the appendix is ​​clearly visible in the direction of the nearest village when the combine captain wants to dine.

Waiting for your comments, questions and suggestions.

Source: https://habr.com/ru/post/186352/


All Articles