How to close connection with mqtt client node.js - node.js

I'm doing a method that works as follows:
When I press a button, I want to be continuously getting messages from the topic I'm subscribed to, and when I press another button I want to close connection and not receive any more messages. I think the connection closes correctly because the client.on('close') event is executed, however I continue receiving messages from the topic.
What should I do to not receive any more messages?
startStop is a boolean, it is true when I want to receive messages and false when I want to stop receiving them. What am I doing wrong?
Here's my code:
mqttClientSubscribeToTraceability(reguladorId, startStop) {
const tls = (Meteor.settings.mqttConfigClient.mqtt.port === 8883) || Meteor.settings.mqttConfigClient.mqtt.ca;
const client = mqtt.connect(`${tls ? 'mqtts' : 'mqtt'}://${Meteor.settings.mqttConfigClient.mqtt.host}:${Meteor.settings.mqttConfigClient.mqtt.port}`,
{ keyPath: Meteor.settings.mqttConfigClient.mqtt.key,
certPath: Meteor.settings.mqttConfigClient.mqtt.cert,
ca: [Meteor.settings.mqttConfigClient.mqtt.ca],
rejectUnauthorized: false
});
const topic = `in/1.0/trafficLights/${global.region}/${reguladorId}`;
const json = {};
if (startStop) {
client.on('connect', () => {
client.subscribe(topic);
});
client.on('message', (topic, message) => {
const msg = JSON.parse(message);
console.log('msg', msg);
});
} else {
client.end();
client.on('close', () => {
client.unsubscribe(topic);
});
}
}

The problem is that you are creating the connection object every time you call the function and not keeping a persistent reference to it.
This means when you call the function with startstop equal to true, you will create a new client object, subscribe to the topic and then attach the message callback. You then throw away the reference to the client object so there is no way to get it back.
When you call the function with startstop set to false, you still create a new client object, but then immediately close it. (Also calling unsubscribe after the client has been closed will achieve nothing except maybe throwing an error).
Given you have another variable (the reguladorId) you probably need to use an object which uses that as the key to hold separate client instances.

Related

How to call FIXAPI using any library (fixparser for nodejs)?

I want to use FIXAPI for one of my application.
I am trying connect "LOGON" api. But i am not getting any error or any Response.
While i was tried with "b2bits simulator", it works.
But Using any library, it does not give me any error or reponse.
I am using "fixparser" library (NodeJS npm library) to call the api.
Any help would be appreciated.
Thanks.
calling fixparser with different versions. and also tried different library (node-quickfix)
var fixparser = require("fixparser");
const fixParser = new FIXParser();
fixParser.connect({
host: HOST,
port: PORT,
protocol: 'tcp',
sender: SENDER,
target: TARGET,
fixVersion: VERSION
});
// Sendlogon function
function sendLogon() {
const logon = fixParser.createMessage(
new Field(Fields.MsgType, Messages.Logon),
new Field(Fields.MsgSeqNum, fixParser.getNextTargetMsgSeqNum()),
new Field(Fields.SenderCompID, SENDER),
new Field(Fields.SendingTime, fixParser.getTimestamp()),
new Field(Fields.TargetCompID, TARGET),
new Field(Fields.ResetSeqNumFlag, 'Y'),
new Field(Fields.EncryptMethod, EncryptMethod.None),
new Field(Fields.HeartBtInt, 10)
);
const messages = fixParser.parse(logon.encode());
fixParser.send(logon);
}
// Open connection
fixParser.on('open', async (s, se) => {
console.log("Started....");
sendLogon();
});
// Retrive response
fixParser.on('message', (message) => {
// Received FIX message
// console.log("message",message);
console.log('received message', message.description, message.string);
});
// Close connection
fixParser.on('close', () => {
console.log("closed");
});
I want to get response and error(if any)
Looks like you are trying to connect to Stock Exchange api. So according to their docs: first message sent by client have to be a LOGON message, but FixParser after opening sockets sends heartbeat instead, and api closes connection after unexpected message.

Tracking WS Clients in Nodejs

I am using ws through an express plugin, express-ws. My back end needs to track clients and, send message only to a specific or clients. Right now, I am just saving the client socket along with a token. So, when the client connects, a message is sent like the following by the client:
{"path":"/openUserSocket","token":"<CLIENT_TOKEN_VALUE>"}
This registers a socket in the back end. The socket is managed by a class wscManager which, saves the socket in a plain object. So, to register a socket, I do this:
// adding web socket support
const expressWs = require('express-ws')(app, null, {
wsOptions: {
clientTracking: true
}
});
let man = new wscManager();
// then in the web socket handler
app.ws('/', (sck,req) => {
sck.on('message',(msg)=>{
// handle messages
if(msg.path === '/openUserSocket'){
man.set(msg.token, sck);
return sck.send(JSON.stringify({ status: 200, message: 'Ok' }));
}
else if(msg.path === '/<SOME_OTHER_PATH>'){
// use that socket
}
// ... other route handling
}
}
Now, as I showed in the code above, I am trying to save the socket for later use. set just uses the token as a key for saving the socket object in another object. Later, get(token) can be used with that token to use the socket. I also extended the express app prototype to allow other route handles to use the wscManager:
app.response._man = man;
Now, my question is this the right approach to client tracking? In other route handlers, the manager is used like this:
// in route handler
res._man.send(JSON.stringify({ status: <STATUS>, message: <MSG> }));
Thanks for your time and patience.

How to manage parallel HTTP request that are based on Message Queuing Listneners (NodeJS)

I'm sure this kind of problem have been resolved here many time but I can't find how those question was formulated.
I have a micro-services that handle the communication between my infrastructure and a MQTT Broker. Every time a HTTP request is received I send a "Who is alive in the room XXX ?" message on the MQTT Broker, and every client registered on the "XXX/alive" topic have to answer and I wait Y milliseconds before closing the request by sending back the responses received to the client.
It works well when I'm handling one request. But it screws up when more than one request is asked at a time.
Here is the Express route handling the HTTP requests :
app.get('/espPassports', (req, res) => {
mqttHelper.getESPPassports(req.query.model_Name).then((passports) => {
res.send(passports).end();
}).catch(err => {
res.send(err).end();
})
})
Here is how the getESPPassports works :
getESPPassports: async (model_Name) => {
return new Promise((resolve, reject) => {
// Say there is a request performed
ongoing_request.isOpen = true;
ongoing_request.model_Name = model_Name;
// Ask who is alive
con.publish(topic, "ASK");
setTimeout(() => {
// If no answer after given timeout
if (ongoing_request.passports.length == 0) {
reject({ error: "No MQTT passports found" });
// Else send a deep clone of the answers (else it's empty)
} else {
resolve(JSON.parse(JSON.stringify(ongoing_request.passports)));
}
// Delete the current request object and 'close it'
ongoing_request.passports.length = 0;
ongoing_request.isOpen = false;
ongoing_request.model_Name = ""
}, process.env.mqtt_timeout || 2000)
})
}
};
And here is the MQTT listener :
con.on("message", (topic, message) => {
// If a passport is received check the topic and if there is a request opened
if (_checkTopic(topic) && ongoing_request.isOpen) {
try {
ongoing_request.passports.push(JSON.parse(message));
} catch (error) {
// do stuff if error
}
}
}
})
I know the problem come from the boolean i'm using to specify if there is a request ongoing, I was thinking to create an object for each new request and identify them by a unique id (like a timetamp) but I have no way to make the MQTT listneners to know this unique id.
I have some other solution in mind but I'm not sure they'll work and I feel like there is a way to handle that nicely that I don't know about.
Have a good day.
You need to generate a unique id for each request and include it in the MQTT message, you can then cache the Express response object keyed by the unique id.
The devices need to include the unique id in their responses so they can be paired up with the right response.
The other approach is just to cache responses from the devices and assign the cache a Time to Live so you don't need to ask the devices every time.

Terminate EventSource event listener?

I'm trying to work around a problem to do with rest streaming between the Nest API and a service (ST) that does not support streaming.
To get around this, I have built a service on Sails which takes a post request from ST containing the Nest Token, and then triggers an EventSource event listener that sends the data back to ST.
It is heavily based off the Nest rest-streaming example here:
https://github.com/nestlabs/rest-streaming and my code is as follows:
startStream: function(req, res) {
var nestToken = req.body.nestToken,
stToken = req.body.stToken,
endpointURL = req.body.endpointURL,
source = new EventSource(sails.config.nest.nest_api_url + '?auth=' + nestToken);
source.addEventListener('put', function(e) {
var d = JSON.parse(e.data);
var data = { devices: d.data.devices, structures: d.data.structures},
config = { headers : {'Authorization': 'Bearer ' + stToken}};
sendData(endpointURL, data, config);
});
source.addEventListener('open', function(e) {
console.log("Connection opened");
});
source.addEventListener('auth_revoked', function(e){
console.log("Auth token revoed");
});
source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
console.error('Connection was closed! ', e);
} else {
console.error('An unknown error occurred: ', e);
}
}, false);
}
};
The problem I foresee though is that once a request is received by the node server, it start the event listener, however I cannot for the life of me figure out how I can kill the event listener.
If I cannot figure out a way to stop this, then every EventListener will run indefinitely which is obviously not suitable.
Has anyone got any suggestions on how to overcome the issue?
Each SSH client connection is a dedicated socket.
If a particular client doesn't want event streaming, don't make the connection. If they start event streaming, but want to turn it off, call source.close();source=NULL;
If from server-side you want to stop sending the messages, close the socket.
You didn't show the server-side code, but if it is running a dedicated process per SSE client then you just exit the process. If you are maintaining a list of sockets, one per connected client, close the socket. On node.js you might be running a function on setInterval. To close the connection you do and clearInterval() and response.end();.

node.js only one client is receiving the messages

As far as I know, unless told otherwise, if the server sends a message, all clients should receive it. But in my case only one client is getting the messages.
client :
(function () {
window.Network = {
socket : null,
initialize : function(socketURL) {
this.socket = io.connect(socketURL);
this.socket.on('new move', this.add);
},
add : function(data) {
var msg = $('<div class="msg"></div>')
.append('<span class="text">' + data.x+'/'+data.y + '</span>');
$('#messages')
.append(msg)
.animate({scrollTop: $('#messages').prop('scrollHeight')}, 0);
},
send : function(data) {
this.socket.emit("move",data);
return false;
}
};
}());
server:
io.sockets.on('connection', function (socket) {
socket.on('move', function (data) {
console.log(data);
socket.emit("new move",data);
});
});
If I open several clients and use "send" function, only the client that sent it receives the emit from the server.
Any idea what I am doing wrong?
Thanks
To emit globally on the server side use this :
io.sockets.emit('new move', 'data1');
To emit to the current socket :
socket.emit('function', 'data1', 'data2');
To broadcast to everyone but the client :
socket.broadcast.emit('function', 'data1', 'data2');
Source
When you are using socket, you are talking directly to the connected client.
You can use rooms however, un this snippet i add the socket to room1
// Add the player socket, to the room.
socket.join('room1');
Then you can emit to all client in a room by
io.sockets.in('room1').emit('startGame', true);
The code is working just as it is supposed to. You are doing socket.emit("new move",data);, which will emit a new move event back to that specific socket, which in your case is the same socket that connected.
If you also want it to be sent to other clients that are connected, then you need to explicitly emit the event to other clients.
socket.emit("new move",data);
socket.broadcast.emit("new move",data);

Resources