Nodejs - data transfer between server and client - node.js

I was given a task to send JSON string from client to server and from server to client, whenever there is a new record found to send.
I decided to build TCP connection(suggest me if there is any other better way in Node.js) between server and client to transfer data.
The problem is, I was supposed to use a delimiter to separate JSON strings one from another. I am afraid what if the json string contains the delimiter string inside the object. I am looking for a better way to separate two JSON strings.
Below is my code. Please help me.
Client
var net = require('net')
, client = new net.Socket();
var chunk = ''
, dlim_index = -1
, delimit = '~~';
client.connect(config.Port, config.IpAddress, function () {
console.log('Server Connected');
client.write('CLIENTID:' + process.argv[2]);
client.write(delimit);
});
client.on('data', function (data) {
var recvData = data.toString().trim();
chunk += recvData;
dlim_index = chunk.indexOf(recvData);
console.log(data);
while (dlim_index > -1) {
var useData = chunk.substring(0, dlim_index);
if (useData == 'SUCCESS') {
controller.listenOutQueue(function (dataToSend) {
var object = JSON.parse(dataToSend);
client.write(dataToSend);
client.write(delimit);
});
}
else {
var record = JSON.parse(useData);
controller.insertIntoQueue(record, function (status) {
});
}
chunk = chunk.substring(dlim_index + 2);
dlim_index = chunk.indexOf(delimit);
}
});
client.on('close', function () {
console.log('Connection closed');
});
client.setTimeout(50000, function () {
//client.destroy();
});
Server
var net = require('net')
, server = net.createServer()
, delimit = '~~'
, clients = [];
controller.listenOutQueue(function (dataToSend) {
client.write(dataToSend);
client.write(delimit);
});
server.on('connection', function (socket) {
var chunk = '';
var dlim_index = -1;
socket.on('data', function (data) {
var recvData = data.toString().trim();
chunk += recvData;
dlim_index = chunk.indexOf(delimit);
while (dlim_index > -1) {
var useData = chunk.substring(0, dlim_index);
if (useData.substring(0, 9) == 'CLIENTID:') {
socket.clientid = useData.replace('CLIENTID:', '');
console.log('Client Id: ' + socket.clientid);
clients.push(socket);
var successMessage = "SUCCESS";
socket.write(successMessage);
socket.write(delimit);
}
else {
controller.insertIntoQueue(JSON.parse(useData), function (status) {
});
}
chunk = chunk.substring(dlim_index + 2);
dlim_index = chunk.indexOf(delimit);
}
});
socket.on('end', function () {
console.log('Connection Closed (' + socket.clientid + ')');
});
socket.on('error', function (err) {
console.log('SOCKET ERROR:', err);
});
});
server.listen(config.Port, config.IpAddress);

Related

Node.js net socket

I'm using the net module to create a listener but I've experienced some issues. I'm trying to make it wait till it's done writing the "text" to the client before the client can type again. If I'm not doing this and I hold in enter it'll just make you able to write enters between text leading to weird formatting etc.
So how could I make it wait till it's written to the client?
Code:
const net = require('net');
const server = new net.Server();
server.on('connection', async function (socket) {
console.log("Client connected!");
socket.on('data', async function (data) {
socket.setEncoding('utf8');
let input = data.toString().replace(/(\r\n|\n|\r)/gm, "");
if (input == "echo")
socket.write("$ ");
else
socket.write("invalid command");
});
});
server.listen(1337, function() {
console.log("listening");
});
Picture:
https://imgur.com/a/lc21Y13
Edit:
This is on localhost, let's say I'd host it on a server so there's a higher ping it's way worse.
Edit:
Here a picture from when it's hosted on a server:
https://imgur.com/a/LIKRRr9
Edit:
I've tried using SSH instead of telnet and raw and got basically the same result now.
Picture:
https://imgur.com/a/XJmpGSa
Code:
var fs = require('fs');
var username = null;
var ssh2 = require('ssh2');
new ssh2.Server({
hostKeys: [fs.readFileSync('ssh.key')]
}, function (client) {
console.log('Client connected!');
client.on('authentication', function (ctx) {
if (ctx.method !== 'password') return ctx.reject(['password']);
if (ctx.method === 'password') {
username = ctx.username;
console.log(username);
console.log(ctx.password);
ctx.accept();
}
else {
console.log("rejected.");
ctx.reject();
}
}).on('ready', function () {
console.log('Client authenticated!');
client.on('session', function (accept, reject) {
var session = accept();
session.once('shell', function (accept, reject, info) {
var stream = accept();
stream.write("$ ");
stream.on('data', function (data) {
var args = data.toString().split(" ");
console.log(args);
switch (args[0]) {
case "echo":
args.shift();
stream.write(args.join(" ") + "\r\n");
break;
case "whoami":
stream.write(username + "\r\n");
break;
case "exit":
stream.exit(0);
stream.end();
stream = undefined;
break;
default:
stream.stderr.write(args[0] + ": No such command!\r\n");
break;
}
if (typeof stream != 'undefined') {
stream.write("$ ");
}
});
});
});
}).on('end', function () {
console.log('Client disconnected');
});
}).listen(1337, function () {
console.log('Listening on port ' + this.address().port);
});
Try This. What this code does is simply buffering until \n enter is received from a client.
const net = require("net");
const readline = require("readline");
const execCommand = (command, args, socket) => {
return new Promise((res, rej) => {
setTimeout(() => {
// to clear the terminal
socket.write("\u001B[2J\u001B[0;0f");
socket.write(
`Executed command: ${command} with args: ${args} and result was: ${Math.random()}`
);
socket.write('\n>')
res();
}, 3000);
});
};
const server = net.createServer((socket) => {
socket.write("Connected");
// nice prompt
socket.write("\n>");
const rl = readline.createInterface({
input: socket,
output: socket,
});
rl.on("line", (line) => {
if (line.length === 0) {
socket.write("No command to execute!");
socket.write('\n>')
return;
}
// destructuring command and args
// E.g. command arg1 arg2 ....
const [command, ...args] = line.split(" ");
execCommand(command, args, socket);
});
});
server.listen(1337, "127.0.0.1");

New connection cause the current connections to stop working

I am making the chat application using socket (which I'm new at) with multiple tenants structure and using namespaces. Here's my code:
Socket server:
index.js
class Server {
constructor() {
this.port = process.env.PORT || 3000;
this.host = process.env.HOST || `localhost`;
this.app = express();
this.http = http.Server(this.app);
this.rootSocket = socketio(this.http);
}
run() {
new socketEvents(this.rootSocket).socketConfig();
this.app.use(express.static(__dirname + '/uploads'));
this.http.listen(this.port, this.host, () => {
console.log(`Listening on ${this.host}:${this.port}`);
});
}
}
const app = new Server();
app.run();
socket.js
var redis = require('redis');
var redisConnection = {
host: process.env.REDIS_HOST,
password: process.env.REDIS_PASS
};
var sub = redis.createClient(redisConnection);
var pub = redis.createClient(redisConnection);
class Socket {
constructor(rootSocket) {
this.rootIo = rootSocket;
}
socketEvents() {
/**
* Subscribe redis channel
*/
sub.subscribe('visitorBehaviorApiResponse');
//TODO: subscribe channel..
// Listen to redis channel that published from api
sub.on('message', (channel, data) => {
data = JSON.parse(data);
console.log(data);
const io = this.rootIo.of(data.namespace);
if (channel === 'visitorBehaviorApiResponse') {
io.to(data.thread_id).emit('receiveBehavior', data);
io.to('staff_room').emit('incomingBehavior', data);
}
})
sub.on('error', function (error) {
console.log('ERROR ' + error)
})
var clients = 0;
this.rootIo.on('connection', (rootSocket) => {
clients++;
console.log('root:' + rootSocket.id + ' connected (total ' + clients + ' clients connected)');
const ns = rootSocket.handshake['query'].namespace;
// Dynamic namespace for multiple tenants
if (typeof (ns) === 'string') {
const splitedUrl = ns.split("/");
const namespace = splitedUrl[splitedUrl.length - 1];
const nsio = this.rootIo.of(namespace);
this.io = nsio;
this.io.once('connection', (socket) => {
var visitors = [];
console.log('new ' + socket.id + ' connected');
// once a client has connected, we expect to get a ping from them saying what room they want to join
socket.on('createChatRoom', function (data) {
socket.join(data.thread_id);
if (typeof data.is_staff !== 'undefined' && data.is_staff == 1) {
socket.join('staff_room');
} else {
if (visitors.some(e => e.visitor_id === data.visitor_id)) {
visitors.forEach(function (visitor) {
if (visitor.visitor_id === data.visitor_id) {
visitor.socket_ids.push(socket.id);
}
})
} else {
data.socket_ids = [];
data.socket_ids.push(socket.id);
visitors.push(data);
}
socket.join('visitor_room');
}
//TODO: push to redis to check conversation type
});
socket.on('sendMessage', function (data) {
console.log(data);
pub.publish('chatMessage', JSON.stringify(data));
this.io.in(data.thread_id).emit('receiveMessage', data);
this.io.in('staff_room').emit('incomingMessage', data);
// Notify new message in room
data.notify_type = 'default';
socket.to(data.thread_id).emit('receiveNotify', data);
}.bind(this))
socket.on('disconnect', (reason) => {
sub.quit();
console.log('client ' + socket.id + ' left, ' + reason);
});
socket.on('error', (error) => {
console.log(error);
});
});
}
// Root disconnect
rootSocket.on('disconnect', function () {
clients--;
console.log('root:' + rootSocket.id + ' disconnected (total ' + clients + ' clients connected)');
});
});
}
socketConfig() {
this.socketEvents();
}
}
module.exports = Socket;
Client:
const server = 'https://socket-server'
const connect = function (namespace) {
return io.connect(namespace, {
query: 'namespace=' + namespace,
resource: 'socket.io',
transports: ['websocket'],
upgrade: false
})
}
const url_string = window.location.href
const url = new URL(url_string)
const parameters = Object.fromEntries(url.searchParams)
const socket = connect(`${server}/${parameters.shopify_domain}`)
var handleErrors = (err) => {
console.error(err);
}
socket.on('connect_error', err => handleErrors(err))
socket.on('connect_failed', err => handleErrors(err))
socket.on('disconnect', err => handleErrors(err))
The problem that I met is when socket server got a new connection, the existing connections will be stopped working util they make a page refreshing to reconnect a new socket.id.
And when a namespace's client emit data, it sends to other namespaces, seem my code is not work correctly in a namespace.
Could you take a look at my code and point me where I'm wrong?
Thanks
1) Get UserId or accessToken while handshaking(in case of accessToken decrypt it).
and store userID: socketId(in Redis or in local hashmap) depends upon the requirement .
2) When u are going to emit to particular user fetch the socketid to that userid from redis or local hashmap
and emit to it.
**io.to(socketId).emit('hey', 'I just met you');**
3) If you are using multiple servers use sticky sessions
4) Hope this will help you

I get length 1 even It didn't connect to socket

let app = require('express')(),
http = require('http').Server(app),
User = require('../models/users');
http.listen(8080, function () {
console.log('Socket IO server has been started on port 8080');
});
let io = require('socket.io')(http);
var socketRoom = {};
io.of('/chat').on('connection', function (socket) {
var ioRooms = io.of('/chat').adapter.rooms;
console.log(ioRooms) // I got length 1 when I print it.
socket.on('requestRandomChat', function (data) {
User.findOne({userToken : data.userToken}, function(err, user){
if(err) socket.emit('db error');
if(!user) socket.emit('db error : user not found');
else {
socket.emit('responseUserProfile', user);
for (var key in rooms){
if (key == ''){
continue;
}
if (rooms[key].length == 1){
var roomKey = key.replace('/', '');
socket.join(roomKey);
io.sockets.in(roomKey).emit('completeMatch', {});
socketRoom[socket.id] = roomKey;
return;
}
}
socket.join(user.userToken);
socketRoom[user.userToken] = socket.userToken;
}
});
});
socket.on('cancelRequest', function (data) {
socket.leave(socketRoom[socket.id]);
console.log(ioRooms);
});
socket.on('sendMessage', function (data) {
console.log(ioRooms);
console.log('sendMessage!');
io.of('/chat').in(socketRoom[socket.id]).emit('receiveMessage', data);
});
socket.on('disconnect', function (data) {
console.log(ioRooms);
var key = socketRoom[socket.id];
socket.leave(key);
io.of('/chat').in(key).emit('disconnect');
var clients = io.of('/chat').clients(key);
for (var i = 0; i < clients.length; i++) {
clients[i].leave(key);
}
});
});
when I trying to get a length of rooms, I get a length 1 with a socketId.
but I've never connected to the room. Is that default room? can somebody explain what's happening with the room socket?
//{ '/chat#2HAMaavywIwZ1B5fAAAD': Room { sockets: {
'/chat#2HAMaavywIwZ1B5fAAAD': true }, length: 1 } }
In socket io each socket is automatically connected to a default room identified by socket ID.
That's why you get length 1.
I hope you find it helpful.
http://socket.io/docs/rooms-and-namespaces/

Custom TCP-Protocol on node.js

how i can implement a custom protocol on a Node.js NET-Client?
The problem is:
I want to connect to a server. That server has an simple protocol.
Each packet has a length-prefix, it means the first byte say how long the packet is.
How i can implement that?
How i can read for example the first byte to get the packet-length to read the other stuff?
var net = require('net');
var client = new net.Socket();
client.connect(2204, 'myexampledomain.com', function() {
console.log('Connecting...', protocol);
});
client.on('data', function(data) {
console.log('DATA: ' + data);
});
client.on('end', function() {
console.log('end');
});
client.on('timeout', function() {
console.log('timeout');
});
client.on('drain', function() {
console.log('drain');
});
client.on('error', function() {
console.log('error');
});
client.on('close', function() {
console.log('Connection closed');
});
client.on('connect', function() {
console.log('Connect');
client.write("Hello World");
});
You'll have to maintain an internal buffer holding received data and check if it has length bytes before slicing the packet from it. The buffer must be concatenated with received data until it has length bytes and emptied on receiving a full packet. This can be better handled using a Transform stream.
This is what I used in my json-rpc implementation. Each JSON packet is lengthPrefixed. The message doesn't have to be JSON – you can replace the call to this.push(JSON.parse(json.toString()));.
var Transform = require('stream').Transform;
function JsonTransformer(options) {
if (!(this instanceof JsonTransformer)) {
return new JsonTransformer(options);
}
Transform.call(this, {
objectMode: true
});
/*Transform.call(this);
this._readableState.objectMode = false;
this._writableState.objectMode = true;*/
this.buffer = new Buffer(0);
this.lengthPrefix = options.lengthPrefix || 2;
this._readBytes = {
1: 'readUInt8',
2: 'readUInt16BE',
4: 'readUInt32BE'
}[this.lengthPrefix];
}
JsonTransformer.prototype = Object.create(Transform.prototype, {
constructor: {
value: JsonTransformer,
enumerable: false,
writable: false
}
});
function transform() {
var buffer = this.buffer,
lengthPrefix = this.lengthPrefix;
if (buffer.length > lengthPrefix) {
this.bytes = buffer[this._readBytes](0);
if (buffer.length >= this.bytes + lengthPrefix) {
var json = buffer.slice(lengthPrefix, this.bytes + lengthPrefix);
this.buffer = buffer.slice(this.bytes + lengthPrefix);
try {
this.push(JSON.parse(json.toString()));
} catch(err) {
this.emit('parse error', err);
}
transform.call(this);
}
}
}
JsonTransformer.prototype._transform = function(chunk, encoding, next) {
this.buffer = Buffer.concat([this.buffer, chunk]);
transform.call(this);
next();
}
JsonTransformer.prototype._flush = function() {
console.log('Flushed...');
}
var json = new JsonTransformer({lengthPrefix: 2});
var socket = require('net').createServer(function (socket) {
socket.pipe(json).on('data', console.log);
});
socket.listen(3000);
module.exports = JsonTransformer;
json-transformer

using WiFly with ws websockets

I have been trying to figure out a way to connect a WiFly from an Arduino to send some accelerometer data to my node.js server. Currently the way that I have it worked out is having three servers:
Http >> This is for clients purposes
Net server >> This is basically for TCP request, this is how my server receives the information from 3. WS websockets >> this takes the data from the Net server and streams it to the client side.
Here is the code:
var http = require('http');
var fs = require('fs');
var path = require('path');
var url = require('url');
var net = require('net');
var sensorData;
var message = {
"data": ''
}
var newValue,
oldValue,
diff;
//Settings
var HTTP_PORT = 9000;
var NET_PORT = 9001;
var WS_PORT = 9002;
//Server
var mimeTypes = {
"html": "text/html",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"png": "image/png",
"js": "text/javascript",
"css": "text/css"
};
http.createServer(function (req, res) {
var fileToLoad;
if (req.url == '/') {
fileToLoad = 'index.html';
} else {
fileToLoad = url.parse(req.url).pathname.substr(1);
}
console.log('[HTTP] :: Loading :: ' + 'frontend/' + fileToLoad);
var fileBytes;
var httpStatusCode = 200;
fs.exists('frontend/' + fileToLoad, function (doesItExist) {
if (!doesItExist) {
console.log('[HTTP] :: Error loading :: ' + 'frontend/' + fileToLoad);
httpStatusCode = 404;
}
var fileBytes = fs.readFileSync('frontend/' + fileToLoad);
var mimeType = mimeTypes[path.extname(fileToLoad).split('.')[1]];
res.writeHead(httpStatusCode, {
'Content-type': mimeType
});
res.end(fileBytes);
});
// console.log("[INIT] Server running on HTTP Port");
}).listen(HTTP_PORT);
proxy.on("close", function(){
console.log("Connection has closed");
});
proxy.on("end", function(){
console.log("Connection has ended");
});
var socket;
var clients = [];
var socketObject;
var server = net.createServer(function (socket) {
socketObject = socket;
socket.name = socket.remoteAddress + ":" + socket.remotePort;
clients.push(socket);
console.log(socket);
socket.write("HTTP/1.1 101", function () {
console.log('[CONN] New connection: ' + socket.name + ', total clients: ' + clients.length);
});
socket.setEncoding('utf8');
socket.on('error', function (data) {
console.log(data);
});
socket.on('end', function () {
console.log('[END] Disconnection: ' + socket.name + ', total clients: ' + clients.length);
});
socket.on('data', function (data) {
console.log('[RECV from ' + socket.remoteAddress + "] " + data);
oldValue = newValue;
newValue = data;
diff = Math.abs(newValue) - Math.abs(oldValue);
console.log(Math.abs(newValue) + '-' + Math.abs(oldValue));
message.data = Math.abs(diff);
console.log('[SAVED] ' + message.data);
});
});
server.listen(NET_PORT, function () {
console.log("[INIT] Server running on NET server port", NET_PORT);
});
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({
port: WS_PORT
});
wss.on('connection', function (ws) {
// ws.send(JSON.stringify(message));
setInterval(function () {
updateXData(ws)
}, 500);
});
function updateXData(ws) {
var newMessage = {
"data": ""
}
newMessage.data = message.data
ws.send(JSON.stringify(newMessage));
}
So the question is: Is there a cleaner way to do this just by using ws to handle the data from the WiFly and then sending it to the client?
Thanks in advance!
Not sure whether this will suit you and might be new to you but you could make use of MQTT, there are free brokers available which are very good and its relatively easy to set up and implement with Arduino equipped with WiFly Shield.
http://mqtt.org/
Hope this helps somewhat!

Resources