UNPKG

rainfall-tcp

Version:
222 lines (194 loc) 7.03 kB
/*jshint esversion: 6 */ var net = require('net'); var udp = require('rainfall-udp'); const BROADCAST_ADDR = "255.255.255.255"; /** @module tcp */ /** @typedef initParams @property {Boolean} [udplisten] - (Optional) True if you want to listen for broadcast messages and false (or undefined) otherwise @property {Number} [rport] - (Optional) The port listened by the TCP (and UDP if you are listening for broadcasts). If undefined, server will listen on arbitrary port. @property {Number} [broadcast_port] - (Optional) The port used when creating a broadcast address. If undefined, a default value (2357) will be used */ /** * Callback used by Driver. * @callback onInitialized * @param {Error} error - If there is a problem initializing this will be an Error object, otherwise will be null * @param {module:tcp~Driver} driver - The driver object */ /** * Callback used by listen. * @callback onMessage * @param {Buffer} message - Buffer containing the buffer received from the network * @param {module:tcp~address} from - Address of the transmitter */ /** * Callback used by listen. * @callback onListening * @param {Error} error - If there is a problem listening this will be an Error object, otherwise will be null */ /** * Callback used by send. * @callback onSent * @param {Error} error - If there is a problem sending this will be an Error object, otherwise will be null */ /** @typedef {Object} address @property {String} address - The IP (v4 or v6) address @property {Number} port - The TCP port @property {String} family - (Optional) The IP version (can be IPv4 or IPv6) */ /** @class Driver **/ function Driver (params, callback) { this._port = params.rport; this._msgCallback = null; this._udpListen = params.udplisten; //TCP server to listen this._tcpServer = net.createServer(); this._tcpServer.on('error', (err) => { if (this._lastCallback) { this._lastCallback(err); this._lastCallback = null; } }); this._tcpServer.on('connection', (socket) => { var buf = null; var error = null; var address = { address: socket.remoteAddress, family: socket.remoteFamily }; socket.on('data', (data) => { if (buf === null) buf = data; else buf = Buffer.concat([buf, data], buf.length + data.length); }); socket.on('end', () => { if (this._msgCallback && buf !== null) { if (!error) { address.port = buf.readUInt32BE(buf.length - 4); this._msgCallback(buf.slice(0, buf.length - 4), address); } } }); socket.on('error', (err) => { error = err; }); }); //Creates UDP for broadcast udp.createDriver(params, (err, udpInstance) => { this._udpDriver = udpInstance; this._port = udpInstance.getAddress().port; if (err) callback(err); else if (callback) callback(null, this); }); } function createDriver(params, callback) { new Driver(params, callback); } /** Opens an TCP socket listening the port and address specified in the rport parameter. Also does that for UDP if you want to listen for broadcast messages. @param {module:tcp~onMessage} msgCallback - Function to be called when a message arrives @param {module:tcp~onListening} [listenCallback] - Function to be called when the driver is listening, if an error occurred */ Driver.prototype.listen = function (msgCallback, listenCallback) { this._lastCallback = listenCallback; this._msgCallback = msgCallback; this._tcpServer.listen({ port: this._port }, () => { this._lastCallback = null; if (this._udpListen) { this._udpDriver.listen(msgCallback, listenCallback); } else if (listenCallback) listenCallback(); }); }; /** Sends an package using TCP. If the address is the broadcast address, sends the package using UDP @param {module:tcp~address} to - Object containing the address object of the recipient @param {Buffer} message - Buffer containing the message to be sent @param {module:tcp~onSent} [callback] - Function to be called when the message was sent */ Driver.prototype.send = function (to, message, callback) { if (to.address === BROADCAST_ADDR) { //Send using udp this._udpDriver.send(to, message, callback); } else { const client = net.createConnection({ port: to.port, host: to.address, }, () => { var portBuffer = new Buffer(4); portBuffer.writeUInt32BE(this._port, 0); client.end(Buffer.concat([message, portBuffer], message.length + portBuffer.length)); }); client.on('end', () => { callback(); }); client.on('error', (err) => { callback(err); }); } }; /** Stops listening for messages. But, if you start listening again, this instance must work */ Driver.prototype.stop = function () { this._tcpServer.close(); this._udpDriver.stop(); }; /** Stops the UDP server, if listen() was called. You can still use the driver to listen and send */ Driver.prototype.close = function () { this._tcpServer.close(); this._udpDriver.close(); }; /** Gets the broadcast network address. @returns {module:tcp~address} Broadcast network address */ Driver.prototype.getBroadcastAddress = function () { return this._udpDriver.getBroadcastAddress(); }; var compareAddresses = function (address1, address2) { return address1.address === address2.address; }; /** Gets the driver network address. Only works when "listening" was called beforehands @returns {module:tcp~address} Network address */ Driver.prototype.getAddress = function() { return this._udpDriver.getAddress(); }; /** Compares two addresses (it is the same compareAddresses exported function) @param {module:tcp~address} a1 - First address to compare @param {module:tcp~address} a2 - Second address to compare @returns {boolean} true if address1 and adress2 are the same and false otherwise */ Driver.prototype.compareAddresses = compareAddresses; /** Creates a driver for TCP socket. It doesn't bind the port. But binds this port to the UDP. @param {module:tcp~initParams} params - Parameters to initialize the tcp driver @param {module:tcp~onInitialized} [callback] - Function to be called when the driver is initialized */ exports.createDriver = createDriver; /** Compares two addresses @param {module:tcp~address} a1 - First address to compare @param {module:tcp~address} a2 - Second address to compare @returns {boolean} true if address1 and adress2 are the same and false otherwise */ exports.compareAddresses = compareAddresses;