MQTT ACK implementation - event listening break - javascript

I am having a trouble in implementing the ACK in order to get feedback that a message was delivered using MQTT. My conception lies on providing an id among the message sent by the sender so that the receiver sends back an ACK with the same id on a different channel. Now the issue occurring is that I cannot break the listening on event when I receive the ack.
So far my code is
let mqtt = require('async-mqtt')
, cfg = require('./cfg');
let client = mqtt.connect(cfg.server);
client.subscribe('some/other/topic');
client.on('connect', sendWithAck)
let id = 123;
async function sendWithAck() {
try {
await client.publish('some/topic', `Message with id${id}`, () => {
client.on('message', (topic, msg) => {
console.log(`${topic}> ${msg.toString()}`);
//this.stopPropagation(); //doesn't work
})
});
await client.end();
console.log('done');
} catch(e) {
console.log('error', e);
process.exit();
}
}

This approach won't work, because what would happen if the other end never responds (e.g. has crashed). There is no way to know this at a MQTT protocol level, unlike a synchronous protocol like HTTP.
The right approach is to set up the on('message') listener before the call to subscribe and to use a state machine to record the id of sent messages and remove them when the response comes in. This way you can set up a timer to allow for time out of responses and deal with them appropriately as well.

Related

Socket.io 4.5.4 acknowledgement is automatically acknowledged when it should fail to be acknowledged

I'm trying to emit an event from the server side to a single socket on the client side. The acknowledgement is received quickly with a response of empty array. However there is nothing client side listening for the event. Emitting with a randomly generated event name has the same result. I have checked that the socket ID is correct. Emitting to a randomly generated socket ID has the same result.
Code:
const eventName = 'unheard-event';
this.io
.to(this.combatantA.socketId)
.timeout(5000)
.emit(eventName, this.combatantA.name, (err, response) => {
if (err) {
// the other side did not acknowledge the event in the given delay
console.log(`${eventName} err`, err);
} else {
console.log(`${eventName} response`, response);
}
});
Response:
I would expect this to timeout and hit the if(err) block of code in every scenario.
I've combed through the socket.io 4.x documentation and it doesn't cover this anywhere that I can find. https://socket.io/docs/v4/emitting-events/#acknowledgements
Can anyone shed some light on what is happening here?

Getting TCP socket specific data on data response

I've written the following function:
let responses = {}
let socks = {}
module.ping = function (port, address) {
//console.log(`Ping function was called - ${address} ${port}`)
if (socks[`${address}:${port}`]) {
//console.log("Using existing socket")
responses[`${address}:${port}`] = false
sock = socks[`${address}:${port}`]
sock.write('PING\n')
console.log(`Sent PING to ${address} ${port}`)
}
else {
sock = new net.Socket();
responses[`${address}:${port}`] = false
sock.connect(port, address, async function() {
sock.write('PING\n')
console.log(`Sent PING to ${address} ${port}`)
});
// Response listeners
sock.on('data', function(data) {
clean_data = data.toString().replace(/\n/g, '').replace(/\r/g, '')
console.log(`[${sock.remoteAddress}:${sock.remotePort}] Received ${clean_data}`)
if (clean_data == 'PONG') {
//console.log(`[${sock.remoteAddress}:${sock.remotePort}] Received PONG`)
//sock.end()
//delete socks[`${address}:${port}`]
responses[`${sock.remoteAddress}:${sock.remotePort}`] = true
}
}
});
sock.on('error', function(error) {
if (sock.remoteAddress) {
responses[`${sock.remoteAddress}:${sock.remotePort}`] = false
}
sock.destroy()
delete socks[`${address}:${port}`]
});
// Add to list of sockets
if (sock) {
socks[`${address}:${port}`] = sock
}
}
}
On the other end, I have a listening TCP server that simple responds with "PONG\n". When I try a single host I get the expected output:
module.ping(1337, 10.0.0.100)
await delay(5000) // Custom function
module.ping(1337, 10.0.0.100)
Sent PING to 10.0.0.100 1337
[10.0.0.100:1337] Received PONG
Sent PING to 10.0.0.100 1337
[10.0.0.100:1337] Received PONG
However when I attempt to hit multiple hosts:
module.ping(1337, 10.0.0.100)
module.ping(1337, 10.0.0.200)
await delay(5000) // Custom function
module.ping(1337, 10.0.0.100)
module.ping(1337, 10.0.0.200)
Sent PING to 10.0.0.100 1337
Sent PING to 10.0.0.200 1337
[10.0.0.200:1337] Received PONG
[10.0.0.200:1337] Received PONG
Sent PING to 10.0.0.100 1337
Sent PING to 10.0.0.200 1337
[10.0.0.200:1337] Received PONG
[10.0.0.200:1337] Received PONG
It seems to me that the event listener for "data" I've added has somehow bound to the incorrect address within my code, however I can't see where. If I add more hosts to the list, the last host with the PING sent is the one that all the PONGs are marked as part of.
For starters, I see no declaration for the sock variable which means it's in some some higher scope and getting wrongly confused or trounced between different asynchronous operations that are both in progress.
Declare that variable locally so each use of it is a separate variable and one async operation won't overwrite the one you were using for another operation. ALL variables, not purposely intended to be higher scoped and shared MUST be declared locally.
I don't know if that is the only problem here, but it IS a problem here. For example, if you call .ping() twice in a row and have to create two new sockets, the second one will overwrite the sock variable before the .connect() succeeds causing you to send the PING to the wrong sock which is exactly what your diagnostic logs show.

Socket.io—about socket disconnect

I have this scenario with socket.io:
I want to receive the data from a sever and Forward the data to webclient.But when I receive a lot of data and close the page, it console
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
DISCONNECTED FROM CLIENT
...(a lot)
Here is the code:
server:
var express=require('express');
var app=express();
var net=require('net');
var http=require('http').createServer(app);
var io=require('socket.io')(http);
var net=require('net');
var nodeServer = new net.Socket();
var aSocket=null;
io.on('connection', function (socketIO) {
aSocket=socketIO;
};
nodeServer.on('data', function(data) {
if(aSocket!=null){
aSocket.emit('pushToWebClient',useData);
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
}
client:
socket.on('pushToWebClient', function (useData) {
});
I find
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
console a lot of'DISCONNECTED FROM CLIENT' but actually it should console just once in the code.
I had even console.log(aSocket.id),it console just only one.
I don't know why it is console so many times.
I haved used setMaxListeners(10) to try to avoid it .
Will it lead to a memory leak?
It appears that you are registering multiple event listeners for the same disconnect event. In this code:
nodeServer.on('data', function(data) {
if(aSocket!=null){
aSocket.emit('pushToWebClient',useData);
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
}
You appear to be registering a new disconnect event listener every time you get a data message. So, if you have multiple listeners, then each one will get called when the socket disconnects and the result is that you will log the same message multiple times all for the same socket.
You can verify this is what is happening by moving your disconnect handler into the connection handler so it is only ever attached just once for each socket.
In addition putting asocket into a global or module-level variable means that your server code would only ever work with one single client at a time. It is not clear exactly what you are trying to do when you get data on the nodeserver connection - whether you're trying to send that data to only one specific client or to all connected clients.
I try to delete the code:
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
or moving it out of nodeServer handler,
it turn into normal and never suggest me to setMaxlisener.
I think maybe it is incorrect put one API into a API
And the envent maybe not release the socket,so it console multiple times .
EDIT: I'm moving this to the top because I saw that someone already provided my solution but you were having a problem managing the data sent to the client. Your aSocket variable will be overwritten by every new client that connects to your app. If you want to send data to a specific client using your server nodeServer, you should create a global variable (an array) that keeps track of all of your client socket connections. So instead of using one global variable aSocket do the following:
var net=require('net');
var nodeServer = new net.Socket();
var clients = [];
io.on('connection', function (socketIO) {
clients.push(socketIO);
var clientNum = clients.length-1;
aSocket.on('disconnect', function () {
clients.splice(clientNum, 1);
console.log('DISCONNECTED FROM CLIENT: '+socketIO.id);
});
};
nodeServer.on('data', function(data) {
//have your data object contain an identifier for the client that caused the handler to fire
//for the sake of the answer I just use data.id
var clientID = data.id;
if(clients[clientID]!=null){
clients[clientID].emit('pushToWebClient', useData);
}
}
Let me know how it goes! My original answer is below:
Try moving
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
out of your nodeServer.on('data', ...) event listener into the io.on('connection', ...) event listener like so:
io.on('connection', function (socketIO) {
aSocket=socketIO;
aSocket.on('disconnect', function () {
console.log('DISCONNECTED FROM CLIENT');
});
};
socket.io is designed to keep polling for the presence of the server/client. If either the server or the client are disconnected, the remaining 'side' continues to receive polling requests and, consequently, will continuously print an error.
You can see this effect on the client side in your browser when you disconnect your server and leave the client page open. If you look at the browser's error/console log what you should see is a continuous stream of net::ERR_CONNECTION_REFUSED errors. By placing the disconnect event handler in the .on('data', ...) handler for your server, you are seeing the converse of this situation.
net:ERR_CONNECTION_REFUSED example
This is basic code for socket.io
The following example attaches socket.io to a plain Node.JS HTTP
server listening on port 3000.
var server = require('http').createServer();
var io = require('socket.io')(server);
io.on('connection', function(client){
client.on('event', function(data){});
client.on('disconnect', function(){});
});
server.listen(3000);
I think, you should try.

Close mubsub client after emitting with socket.io-mongodb-emitter

I'm trying to emit some message into a socket.io room using socket.io-mongodb-emitter. My problem is that, my script never exists, because it keeps a connection alive to mongodb.
Please check my examples below, and give me some advice, how can I make the script close mongodb connection and let itself exit.
emitter.js this script emits the message, the client gets it, but the process doesn't exit.
var io = require('socket.io-mongodb-emitter')("mongodb://localhost:27017/test");
io.in('someRoom').emit('someMessage');
emitter-2.js this script exits, but the client never gets the message.
var io = require('socket.io-mongodb-emitter')("mongodb://localhost:27017/test");
io.in('someRoom').emit('someMessage');
io.client.close();
emitter-3.js this one works perfectly, the client gets the message, and the process exits. But setTimeout is an extremely bad solution, there must be some proper way to let this process exit by itself.
var io = require('socket.io-mongodb-emitter')("mongodb://localhost:27017/test");
io.in('someRoom').emit('someMessage');
setTimeout(function () {
io.client.close();
},100);
Maybe use a callback to get an acknowledgement that the client received the message, then call the close function
server:
var message = "hello";
io.in('someRoom').emit('someMessage', message, function(data){
console.log(data);
io.client.close();
});
client:
socket.on('someMessage', function(message, callback){
// do something with message here
callback('message received');
});
You need to use the .emit's ability to receive an acknowledgement that the message was sent.
Do like so:
io.in('someRoom').emit('someMessage',function(){
console.log('message was sent');
io.client.close();
);
On Server you need to call the function when it is received.
socket.on('someMessage', function (name, fn) {
fn('received');
});
Documentation here

Send message to specific client with socket.io and node.js

I'm working with socket.io and node.js and until now it seems pretty good, but I don't know how to send a message from the server to an specific client, something like this:
client.send(message, receiverSessionId)
But neither the .send() nor the .broadcast() methods seem to supply my need.
What I have found as a possible solution, is that the .broadcast() method accepts as a second parameter an array of SessionIds to which not send the message, so I could pass an array with all the SessionIds connected at that moment to the server, except the one I wish send the message, but I feel there must be a better solution.
Any ideas?
Ivo Wetzel's answer doesn't seem to be valid in Socket.io 0.9 anymore.
In short you must now save the socket.id and use io.sockets.socket(savedSocketId).emit(...) to send messages to it.
This is how I got this working in clustered Node.js server:
First you need to set Redis store as the store so that messages can go cross processes:
var express = require("express");
var redis = require("redis");
var sio = require("socket.io");
var client = redis.createClient()
var app = express.createServer();
var io = sio.listen(app);
io.set("store", new sio.RedisStore);
// In this example we have one master client socket
// that receives messages from others.
io.sockets.on('connection', function(socket) {
// Promote this socket as master
socket.on("I'm the master", function() {
// Save the socket id to Redis so that all processes can access it.
client.set("mastersocket", socket.id, function(err) {
if (err) throw err;
console.log("Master socket is now" + socket.id);
});
});
socket.on("message to master", function(msg) {
// Fetch the socket id from Redis
client.get("mastersocket", function(err, socketId) {
if (err) throw err;
io.sockets.socket(socketId).emit(msg);
});
});
});
I omitted the clustering code here, because it makes this more cluttered, but it's trivial to add. Just add everything to the worker code. More docs here http://nodejs.org/api/cluster.html
each socket joins a room with a socket id for a name, so you can just
io.to('socket#id').emit('hey')
docs: http://socket.io/docs/rooms-and-namespaces/#default-room
The simplest, most elegant way
verified working with socket.io v3.1.1
It's as easy as:
client.emit("your message");
And that's it. Ok, but how does it work?
Minimal working example
Here's an example of a simple client-server interaction where each client regularly receives a message containing a sequence number. There is a unique sequence for each client and that's where the "I need to send a message to a particular client" comes into play.
Server
server.js
const
{Server} = require("socket.io"),
server = new Server(8000);
let
sequenceNumberByClient = new Map();
// event fired every time a new client connects:
server.on("connection", (socket) => {
console.info(`Client connected [id=${socket.id}]`);
// initialize this client's sequence number
sequenceNumberByClient.set(socket, 1);
// when socket disconnects, remove it from the list:
socket.on("disconnect", () => {
sequenceNumberByClient.delete(socket);
console.info(`Client gone [id=${socket.id}]`);
});
});
// sends each client its current sequence number
setInterval(() => {
for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
client.emit("seq-num", sequenceNumber);
sequenceNumberByClient.set(client, sequenceNumber + 1);
}
}, 1000);
The server starts listening on port 8000 for incoming connections. As soon as a new connection is established, that client is added to a map that keeps track of its sequence number. The server also listens for the disconnect event to remove the client from the map when it leaves.
Each and every second, a timer is fired. When it does, the server walks through the map and sends a message to every client with their current sequence number, incrementing it right after. That's all that is to it. Easy peasy.
Client
The client part is even simpler. It just connects to the server and listens for the seq-num message, printing it to the console every time it arrives.
client.js
const
io = require("socket.io-client"),
ioClient = io.connect("http://localhost:8000");
ioClient.on("seq-num", (msg) => console.info(msg));
Running the example
Install the required libraries:
npm install socket.io#3.1.1 socket.io-client#3.1.1
Run the server:
node server
Open other terminal windows and spawn as many clients as you want by running:
node client
I have also prepared a gist with the full code here.
Well you have to grab the client for that (surprise), you can either go the simple way:
var io = io.listen(server);
io.clients[sessionID].send()
Which may break, I doubt it, but it's always a possibility that io.clients might get changed, so use the above with caution
Or you keep track of the clients yourself, therefore you add them to your own clients object in the connection listener and remove them in the disconnect listener.
I would use the latter one, since depending on your application you might want to have more state on the clients anyway, so something like clients[id] = {conn: clientConnect, data: {...}} might do the job.
You can use
//send message only to sender-client
socket.emit('message', 'check this');
//or you can send to all listeners including the sender
io.emit('message', 'check this');
//send to all listeners except the sender
socket.broadcast.emit('message', 'this is a message');
//or you can send it to a room
socket.broadcast.to('chatroom').emit('message', 'this is the message to all');
In 1.0 you should use:
io.sockets.connected[socketid].emit();
Whatever version we are using if we just console.log() the "io" object that we use in our server side nodejs code, [e.g. io.on('connection', function(socket) {...});], we can see that "io" is just an json object and there are many child objects where the socket id and socket objects are stored.
I am using socket.io version 1.3.5, btw.
If we look in the io object, it contains,
sockets:
{ name: '/',
server: [Circular],
sockets: [ [Object], [Object] ],
connected:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
here we can see the socketids "B5AC9w0sYmOGWe4fAAAA" etc. So, we can do,
io.sockets.connected[socketid].emit();
Again, on further inspection we can see segments like,
eio:
{ clients:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
So, we can retrieve a socket from here by doing
io.eio.clients[socketid].emit();
Also, under engine we have,
engine:
{ clients:
{ B5AC9w0sYmOGWe4fAAAA: [Object],
'hWzf97fmU-TIwwzWAAAB': [Object] },
So, we can also write,
io.engine.clients[socketid].emit();
So, I guess we can achieve our goal in any of the 3 ways I listed above,
io.sockets.connected[socketid].emit();
OR
io.eio.clients[socketid].emit();
OR
io.engine.clients[socketid].emit();
You can do this
On server.
global.io=require("socket.io")(server);
io.on("connection",function(client){
console.log("client is ",client.id);
//This is handle by current connected client
client.emit('messages',{hello:'world'})
//This is handle by every client
io.sockets.emit("data",{data:"This is handle by every client"})
app1.saveSession(client.id)
client.on("disconnect",function(){
app1.deleteSession(client.id)
console.log("client disconnected",client.id);
})
})
//And this is handle by particular client
var socketId=req.query.id
if(io.sockets.connected[socketId]!=null) {
io.sockets.connected[socketId].emit('particular User', {data: "Event response by particular user "});
}
And on client, it is very easy to handle.
var socket=io.connect("http://localhost:8080/")
socket.on("messages",function(data){
console.log("message is ",data);
//alert(data)
})
socket.on("data",function(data){
console.log("data is ",data);
//alert(data)
})
socket.on("particular User",function(data){
console.log("data from server ",data);
//alert(data)
})
As of version 1.4.5, be sure you provide a properly prefixed socketId in io.to().
I was taking the socketId the Client logged to debug and it was without prefix so I ended up searching forever till I found out! So you might have to do it like this if the Id you have is not prefixed:
io.to('/#' + socketId).emit('myevent', {foo: 'bar'});
io.sockets.sockets[socket.id].emit(...) worked for me in v0.9
Also you can keep clients refferences. But this makes your memmory busy.
Create an empty object and set your clients into it.
const myClientList = {};
server.on("connection", (socket) => {
console.info(`Client connected [id=${socket.id}]`);
myClientList[socket.id] = socket;
});
socket.on("disconnect", (socket) => {
delete myClientList[socket.id];
});
then call your specific client by id from the object
myClientList[specificId].emit("blabla","somedata");
Socket.IO allows you to “namespace” your sockets, which essentially means assigning different endpoints or paths.
This might help:
http://socket.io/docs/rooms-and-namespaces/

Categories