UNPKG

web-remote-control

Version:

Fast, real-time, remote control of devices (drones, boats, etc) from the web.

254 lines (209 loc) 7.78 kB
/********************************************************************* * * * Copyright 2016 Simon M. Werner * * * * Licensed to the Apache Software Foundation (ASF) under one * * or more contributor license agreements. See the NOTICE file * * distributed with this work for additional information * * regarding copyright ownership. The ASF licenses this file * * to you under the Apache License, Version 2.0 (the * * "License"); you may not use this file except in compliance * * with the License. You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, * * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * * * *********************************************************************/ 'use strict'; var EventEmitter = require('events').EventEmitter; var util = require('util'); var messageHandler = require('./messageHandler'); var SOCKET_IO_PORT = 33331; /** * The connection manager will handle the TCP and UDP transport. As well as * the protocol. * @param {object} options (optional) */ function ServerConnection (options) { this.log = options.log; this.port = options.port; this.proxyUrl = options.proxyUrl; if (!(options.udp4 === false)) { this.listenUDP4(); } if (!(options.tcp === false)) { this.listenTCP(); } if (!(options.socketio === false)) { this.listenSocketIO(); } EventEmitter.call(this); } util.inherits(ServerConnection, EventEmitter); /** * Start the SocketIO server on the given port and address (optional) * @param {number} port The port number to listen on. * @param {string} address (optional) The IP address to listen on. */ ServerConnection.prototype.listenSocketIO = function() { var socketio = require('socket.io'); var self = this; this.socketio = socketio(); this.socketio.on('connection', function(socket) { var socketInfo = { protocol: 'socketio', address: socket.client.conn.remoteAddress, port: '????', socketId: socket.id, socket: socket, close: socket.close }; socket.on('event', function(message) { // Socket.IO sends messages a bit different if (message && message.type === 'Buffer') { message = new Buffer(message.data); } self.handleMessage.bind(self)(message, socketInfo); }); socket.on('close', function() { self.emit('socket-close', socketInfo.socketId); }); }); this.socketio.on('error', this.handleError); this.socketio.listen(SOCKET_IO_PORT); // Wait until the event listener is attached to THIS. setTimeout(function () { self.emit('listening', SOCKET_IO_PORT, self.proxyUrl, 'socketio'); }, 100); }; /** * Start the UDP server on the given port and address (optional). */ ServerConnection.prototype.listenUDP4 = function() { var dgram = require('dgram'); this.udp4 = dgram.createSocket('udp4'); this.udp4.on('error', this.handleError); var self = this; this.udp4.on('message', function (message, remote) { var socketInfo = { protocol: 'udp4', address: remote.address, port: remote.port, socketId: remote.address + ':' + remote.port, close: function () {} }; self.handleMessage.bind(self)(message, socketInfo); }); this.udp4.on('listening', function () { self.emit('listening', self.port, self.proxyUrl, 'udp4'); }); this.udp4.bind(self.port); }; /** * Start the TCP server on the given port and address (optional). */ ServerConnection.prototype.listenTCP = function() { var net = require('net'); var split = require('split'); var self = this; this.tcp = net.createServer(function(socket) { var socketInfo = { protocol: 'tcp', address: socket.remoteAddress, port: socket.remotePort, socketId: socket.remoteAddress + ':' + socket.remotePort, socket: socket, close: function close() { try { socket.destroy(); } catch (ex) { // okay if there is an error - socket may already be closed. } } }; var stream = socket.pipe(split('\n')); stream.on('data', function(message) { self.handleMessage.bind(self)(message, socketInfo); }); stream.on('close', function() { self.emit('socket-close', socketInfo.socketId); }); stream.on('error', self.handleMessage.bind(self)); }); this.tcp.on('error', this.handleError); this.tcp.on('listening', function () { self.emit('listening', self.port, self.proxyUrl, 'tcp'); }); this.tcp.listen(self.port); }; ServerConnection.prototype.handleError = function(err) { if (typeof this.log === 'function') { this.log(err); } else { console.error(err); } this.emit('error', err); }; ServerConnection.prototype.handleMessage = function(message, socketInfo) { var enable_compression = socketInfo.protocol === 'udp4'; var msgObj; try { msgObj = messageHandler.parseIncomingMessage(message, enable_compression); } catch (ex) { this.emit('error', ex); return; } // Empty packet arrived, this happens when remote closes connection if (msgObj === null) { return; } msgObj.socket = socketInfo; this.emit(msgObj.type, msgObj); this.log(new Date(), socketInfo.address + ':' + socketInfo.port, msgObj.type, msgObj.channel || msgObj.uid, msgObj.seq, msgObj.data); }; /** * Close all connections. */ ServerConnection.prototype.closeAll = function() { if (this.udp4) { this.udp4.close(); } if (this.tcp) { this.tcp.close(); } this.removeAllListeners(); }; /** * Sends a message to the remote device. * @param {string} err The error string * @param {string} address The remote address. * @param {number} remote The remote port. */ ServerConnection.prototype._send = function(msgObj) { var socketInfo = msgObj.socket; var enable_compression = socketInfo.protocol === 'udp4'; var msgComp = messageHandler.packOutgoingMessage(msgObj, enable_compression); if (socketInfo.protocol === 'udp4') { this.udp4.send(msgComp, 0, msgComp.length, socketInfo.port, socketInfo.address); return; } if (socketInfo.protocol === 'tcp') { try { socketInfo.socket.write(msgComp); } catch (ex) { this.log('ServerConnection._send():error: ', ex); } return; } if (socketInfo.protocol === 'socketio') { socketInfo.socket.emit('event', msgComp); return; } }; module.exports = ServerConnection;