I have developed a angular app and a server using express running on node. I used socket.io to establish communication between the two over sockets. To test the latency between the two, I did a round trip test where I calculated time when the message was sent by server, the time received at client and then at the time server receives the final response. One abnormality I came across was when I checked these values generated, the time reported by angular app consistently remains to be offset. I got similar results when I had both the server and the angular app running on the same device and two different devices over a VPN. How can I fix this issue?
Event Diagram (Green: Angular app, Blue: Server)
Here is some time data I have collected of the same:
(Sent -> time when msg sent by server, UI -> time when angular app received and sent response, Recv -> time when server received response from angular app)
Sent: 1659354564134 UI: 1659354564158 Recv: 1659354564148
Sent: 1659354569144 UI: 1659354569157 Recv: 1659354569147
Sent: 1659354574147 UI: 1659354574160 Recv: 1659354574150
Sent: 1659354579159 UI: 1659354579173 Recv: 1659354579162
Server Code(index.js):
io.on("connection", (socket) => {
n = 0;
console.log("A user connected with id: %s", socket.id);
socket.on("message", (data) => {
data = JSON.parse(data);
let now = Date.now();
console.log("Sent: %s UI: %s Recv: %s", data["sent"], data["recv"], now);
});
let interval = 5000;
// For sending timestamp only continuously at a set interval
setInterval(() => {
let now = Date.now();
socket.send(JSON.stringify({ sent: now }));
console.log(now, "^");
}, interval);
});
Client Code(Angular: communication.service.ts):
export class CommunicationService {
public socket!: Socket;
public date = new Date();
public serverTimeOffset = 0;
public connect() {
this.socket = io('http://localhost:3000');
this.socket.on('connect', () => {
console.log('Connected');
});
this.socket.on('disconnect', () => {
console.log('Disconnected');
});
this.socket.on('connect_error', () => {
setTimeout(() => {
this.socket.connect();
}, 1000);
});
this.socket.on('message', (event) => {
let data = JSON.parse(event);
let now = Date.now();
this.socket.send(JSON.stringify({ sent: data['sent'], recv: now }));
}
});
}
}
Related
I have been encountering an issue for a long time but I haven't found any solution yet, despite reading the protocol version 5 standard and the emqx documentation.
I want to publish messages with a time limit to simulate a situation where my device is unavailable so that, after the time limit has expired, the broker will delete the message and my device will not receive it.
I want the reason that my device will not be available (and therefore will not receive messages), to be because it is in a closed area, like a tunnel, or in a area with no cellular cove-range and not because it initiated a disconnect from the broker or because the “keepalive” value has expired.
My understanding is that I can use the “messageExpiryInterval” property (in protocol 5) to implement my goal.
I used EMQX broker as follows:
const connectUrl = 'mqtt://broker.emqx.io:1883';
Along with the following connection configuration:
const client = mqtt.connect(connectUrl, {
clientId: 'mqtt_dani_pub',
protocolVersion: 5,
keepalive: 1800,
clean: true
});
And when sending a message, I put the following values:
const options = {
qos: 0,
retain: false,
properties: { messageExpiryInterval: 30 }
};
As you can see, I used a high value, 1800, for “keepalive” to ensure that the device will be connected to the broker for a long time.
To simulate this situation, I used one publisher on one PC, and one subscriber on another PC.
The scenario is as follows:
Connect publisher and subscriber to emqx broker
a. Verify MQTT v5 protocol.
Publish the message (when the subscriber is connected) to emqx with MessageExpiryInterval: 30
a. Subscribe receive the message
Turn OFF the Wi-Fi on the subscriber computer.
Publish the message to emqx with MessageExpiryInterval: 30
Wait for 120 seconds
Turn ON the Wi-Fi on the subscriber computer.
a. Subscriber does not receive the message ==> but it does get the message!
In addition to this, I saw in the standard of protocol 5 section 3.3.2.3.3 (Message Expiry Interval - https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.pdf ) that the PUBLISH packet sent to a Client by the Server MUST contain a Message Expiry Interval set to the received value minus the time that the Application Message has been waiting in the Server, so this may be the problem.
my publisher js code:
import mqtt, { MqttClient } from 'mqtt';
import * as readline from 'node:readline'
import { stdin, stdout } from 'process';
const connectUrl = 'mqtt://broker.emqx.io:1883';
const clientId = 'mqtt_dani_pub';
const topic = 'dani/test';
const subject = 'Publisher';
const rl = readline.createInterface({
input:stdin,
output:stdout
});
const client = mqtt.connect(connectUrl, {
clientId,
protocolVersion: 5,
keepalive: 1800,
clean: true
});
client.on('connect', () => {
console.log(`${subject} client connected..`)
client.subscribe([topic], () => {
console.log(`Subscribe to topic '${topic}'`);
})
});
const options = {
qos: 0,
retain: false,
properties: { messageExpiryInterval: 30 }
};
const publishMsg = (message) => {
client.publish(topic,
`${clientId} - ${Date.now()} - ${message}`,
options,
(error) => {
if (error) {
console.error(error)
}
}
);
};
const input2topic = () => {
return new Promise(resolve => {
rl.question(`send message to topic ${topic}: `,
(input) => {
if(input !== 'exit'){
console.log(`writing to topic ${topic}..`);
publishMsg(input);
resolve(true);
} else{
console.log('exit...');
resolve(false);
}
});
});
}
const main = async () => {
publishMsg('first message');
let flag = true;
while(flag){
await new Promise(resolve => setTimeout(resolve, 1000));
flag = await input2topic();
}
rl.close();
client.end();
}
main();
my subscriber js code:
import mqtt, { MqttClient } from 'mqtt';
const connectUrl = 'mqtt://broker.emqx.io:1883';
const clientId = 'mqtt_dani_sub';
const topic = 'dani/test';
const subject = 'Subscriber';
const client = mqtt.connect(connectUrl, {
clientId,
keepalive: 1800,
protocolVersion: 5,
})
client.on('connect', () => {
console.log(`${subject} client connected`)
client.subscribe([topic], {qos: 0}, () => {
console.log(`Subscribe to topic '${topic}'`)
})
});
client.on('message', (topic, payload, packet) => {
console.log('\nReceived Message:', {
...packet,
message: payload.toString(),
msg_length: payload.toString().length,
time: new Date(),
});
});
I am getting the event emitter leak after using my code 10 times essentially. I understand the default of event emitter auto sending out a warning in the console. My question is what in this code is directly creating the event listeners? Is it poor coding on my part or is it just how the websockets are stacked onto each other?
I'll explain the code a bit. I have one websocket within another and I figured it would serve the data to a web page essentially flowing from Twitch to a localhost site. However, if I use the keywords more than 10 times, I get the error. I do not understand enough about WebSockets to really understand why my code creates a new listener with each msg.text received so anyone with a bit more understanding please help!
I believe me issue to be similar to this though I am having a hard time conceptualizing my own code here
const { paintballShot } = require('./JavaScript/paintballGunFire');
const { readPin } = require('./JavaScript/readPin');
const ws = require('ws');
const express = require('express');
const app = express();
//CONNECT TO TWITCH
let client = new ChatClient({
connection: {
type: "websocket",
secure: true,
}
});
//connected?
client.on("ready", () => console.log("Successfully connected to chat"));
client.on("close", (error) => {
if (error != null) {
console.error("Client closed due to error", error);
}
});
//create headless websocket
const wsServer = new ws.Server({ noServer: true });
wsServer.on('connection', function connection(socket) {
//call other websocket connected to Twitch from inside the new websocket
client.on("PRIVMSG", (msg, error) => {
if (msg.messageText === "right") {
socket.send(JSON.stringify(`${msg.displayName}: ${msg.messageText}`));
}
if (msg.messageText === "left") {
socket.send(JSON.stringify(`${msg.displayName}: ${msg.messageText}`));
}
if (msg.messageText === "fire") {
socket.send(JSON.stringify(`${msg.displayName}: ${msg.messageText}`));
paintballShot();
}
if (msg.messageText === "pin") {
readPin();
}
process.on('uncaughtException', function (err) {
console.log(err);
});
});
client.connect();
client.join("channel");
socket.on('message', message => console.log(message));
});
// `server` is a vanilla Node.js HTTP server
const server = app.listen(3000);
server.on('upgrade', (request, socket, head) => {
wsServer.handleUpgrade(request, socket, head, socket => {
wsServer.emit('connection', socket, request);
});
});
process.on('uncaughtException', function (err) {
console.log(err);
});
To wrap this up, the library I am using (Dank TwitchIRC) does have a connection rate limiter that seems to work if you add it to your chat client in the beginning. If I set it low enough, depending on the messages received from Twitch, it will end connections just as fast, meaning no memory leak.
I'm trying to develop a Node.js server that acts as a metronome (sends a repeated timing message) for everybody connected. I have socket.io rooms working so clients can "subscribe" to different metronomes, but sync between them drifts wildly. I've been using multiple setInterval() calls to schedule timing messages, and I understand that it isn't a very reliable clock source:
Client:
const socket = io('localhost:8000', {});
socket
.on('connect', function() {
console.log('connected');
})
.on('message', function(payload) {
console.log(payload);
});
// window.max is specific to the Max/MSP web browser object.
// This basically just subscribes to room 'inst0' or 'inst1'.
window.max.bindInlet('subscribe', function(inst) {
socket.emit('subscribe', inst);
});
Server:
io.sockets
.on('connection', (socket) => {
console.log('max connected!');
socket
.on('subscribe', (payload) => {
console.log('subscription: ' + payload);
Object.keys(socket.rooms).forEach(key => socket.leave(key));
socket.join('inst'+payload);
});
});
setInterval(() => {
io.to('inst0').emit('message', 'bang');
}, 500);
setInterval(() => {
io.to('inst1').emit('message', 'bang');
}, 250);
Are there other strategies for event scheduling like this? In a latency-free world the messages would have sample-accurate timing (44.1kHz resolution), but I'd be ecstatic to get something even around 30Hz.
TL;DR - How to prevent client from receiving its own messages?
So I'm playing with socket.io after my experience with apollo and graphql.
My simple server looks like this:
io.on('connection', (socket) => {
console.log('New connection established.');
socket.on('disconnect', () => {
console.log('User disconnected.');
});
// Projects:
socket.on('join project', (data) => {
console.log(`User (${data.user.email}) join project with ID ${data.project.id}`);
socket.join(data.project.id);
});
socket.on('leave project', (data) => {
socket.leave(data.project.id);
});
socket.on('change field', (data) => {
console.log('Field was changed:', data);
const { project } = data;
socket.to(project.id).broadcast.emit('field changed', data);
});
});
I'm emitting something like this inside my application:
socket.emit('change field', {
project: {
id: 1,
},
value: 'Hello world',
usersEmail: 'example#email.com',
fieldName: 'description',
});
socket.on('field changed', (data) => {
// if (data.usersEmail === 'example#email.com') return; // This would stop from receiving own messages
console.log('CLIENT: field was changed!', data);
});
What I thought would happen is (due to the broadcast flag that I set up in the on('change field', ...)):
Clients A emits the message
Clients other than A receive the message
What is happening is a log inside other clients and client A itself, saying that the field was changed. Am I missing something?
I had the exact same problem. Couldn't (or didn't try hard enough) to find a setting for it, so instead just added this to my clients on page load:
document.windowid = Math.round(Math.random() * 1000000000000);
Then, add this to the message you emit in your client:
windowid: document.windowid
Then, when you accept data on the client, only do the action when windowid is not the same:
if (message.windowid != document.windowid)
It's not great and socket.io should take care of this issue, but this is the solution I used in my app :)
I am building a chat app with React, Node/Express and socket.io. I have my sockets successfully set to my express server via http.createServer. I have a listener on client and server listening for new messages coming into the chat room. Ideally, I want each instance of the chat to be updated when there is an additional message, like any chat room that ever existed :)
Now I have a successful listen between client and server. I know because of a console.log server-side. However, I am not re-rendering the chat component when I submit a new message from a different instance.
So my code in my client-side (again React) component is as follows and I am using the socket CDN with script tags in my index.html (script tags not shown):
Socket CDN here
var socket = io('')
So that is the socket you see client side :
componentDidMount() {
return axios.get(`api/messages`)
.then((result) => {
if (result.data.length) {
this.setState({
messages: [ ...this.state.messages, ...result.data]
} , () => {
console.log("The state after messages are mounted : ", this.state)
})
}
})
.catch((err) => { throw err})
socket.on('new message', msg => {
this.newMessage(msg);
})
};
newMessage(msg) {
this.setState({
messages: [...this.state.messages, msg]
}, () => {
this.setState({ message: '' })
return this.scrollToBottom()
});
};
onSubmitMessage(event) {
event.preventDefault();
const content = this.state.message;
const msg = {
content,
createdAt : new Date(),
userId : "one",
chatRoomId : "two"
}
axios.post(`api/messages/`, msg)
.then(() => {
this.newMessage(msg);
socket.emit('new message', msg); //HERE'S THE SOCKETS IN ACTION
})
};
Here is the server-side code Node/Express:
//in server.js
const io = new socketIo(server)
require('./socketEvents')(io);
const connections = [];
Then a separate file for my socket events
//in socketEvents.js
module.exports = (io) => {
io.on('connection', (socket) => {
console.log("Beautiful sockets are connected")
socket.once('disconnect', () => {
console.log("socket is disconnected");
});
//DOESN'T DO ANYTHING YET
socket.on('join global', (username) => {
socket.join(username);
console.log("New user in the global chat : ", username)
});
socket.on('new message', (msg) => {
console.log("The new message from sockets : ", msg);
socket.emit('new message', msg.content);
});
});
}
My sockets server side are linked up with the client. I'm just not seeing new messages in different instances. Is it because I'm not re-rendering after the server receives the message?
Thanks in advance, please let me know if you need me to clarify anything.
Cheers!
I figured it out... I'm going to leave this post up with a walkthrough in an attempt to help others who are having trouble with sockets. I may post a blog about it. Will update if I do.
So the code listens on the client side for a message to be sent inside of my onSubmitMessage function.
onSubmitMessage(event) {
event.preventDefault(); //prevents HTML <form> from going on its own post
const content = this.state.message;
//Create message object
const msg = {
content,
createdAt : new Date(),
userId : "one",
chatRoomId : "two"
}
//HERE'S THE IMPORTANT PART!!!
axios.post(`api/messages/`, msg)
.then(() => {
// wrapped in a promise, send a handler to server called
// ('new message') with the message object
this.newMessage(msg);
socket.emit('new message', msg);
})
.then(() => {
//Another promise then waits for the handler to come back from server
//*****IMPORTANT*************
//Then invoke newMessage function to get the post on all sockets
socket.on('message', (msg) => {
this.newMessage(msg);
})
})
};
Now on the server side this is what's happening:
// This is where the listener is for the client side handle
socket.on('new message', (msg) => {
// broadcast.emit will send the msg object back to client side and
// post to every instance expcept for the creator of the message
socket.broadcast.emit('message', msg);
});
SO the data path is (C) for client, (S) for server:
receive message object from user and -------->
(C)socket.emit('new message') -----> (S) socket.on('new message') -------> (S) socket.broadcast.emit('message') --------> (C)socket.on('message')
Back in the client side, I can invoke my newMessage function, which will set the message to state so I can display it.
I hope someone finds this useful! Surprisingly, this seems to go relatively unanswered on Stack. If anyone has any questions feel free to ask!