I'm trying to figure out how to implement a two separate websockets together. Not sure if this possible or not, but I have a websocket that works on it's own in from node.js file to angular another node.js file that uses Kraken (crypto exchange) websocket that also works in it's own file. I'm trying to consolidate them both together so that whenever a event onChange comes from Kraken websocket, I can relay that data to angular with angular socket.io websocket. Trying to do something like this
const webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
let io = require("socket.io")(server, {
cors: {
origin: "http://localhost:4200",
methods: ["GET", "POST"],
allowedHeaders: ["*"],
credentials: true,
},
});
io.on("connection", (socket) => {
const changes = parseTrades(data);
socketIo.sockets.emit(connection.change, changes);
Log whenever a user connects
console.log("user connected");
socket.emit("test event", JSON.stringify(changes));
});
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
});
Although doing this doesnt relay the data to frontend and gives me a memory leak. Is there some way I can accomplish this?
I would probably split up the task a little bit. So have a service for the kraken websocket and maybe a service for your own socket, then have them communicate via observables, that you can also tap into from the front end to display data you want.
#Injectable()
export class KrakenService{
private webSocketClient : WebSocket | null;
private messages$ = new BehaviorSubject<any>(null); // give it a type
openConnection(){
// is connectionUrl from environment ??
this.webSocketClient = new WebSocket(connectionURL);
webSocketClient.on("open", function open() {
/// is webSocketSubscription from environment ??
webSocketClient.send(webSocketSubscription);
});
webSocketClient.on("message", function incoming(wsMsg) {
const data = JSON.parse(wsMsg);
this.messages$.next(data);
console.log("DATA HERE", data[0]);
});
webSocketClient.on("close", function close() {
console.log("kraken websocket closed");
this.webSocketClient = null;
});
}
getKrakenMessages(){
if(webSocketClient == null) this.openConnection();
return messages$.asObserbable();
}
}
So now when you want to either read the web socket messages or use them with the other socket you just subscribe to the krakenService.getKrakenMessages();
Then you can do something similar with you local service as well. Have something that opens connections, and one that emits messages. The example you showed it would open up a connection every time you got a message, so keep those logics separated. And reuse existing connection.
Related
I am facing this weird issue. I am not a veteran of using Socket.io. I have been exploring this library as the app I am building needs a remote playing feature wherein players create invitations to other players so that they can use those invitations to join the game remotely. I am using React on the front end (client-side), and on the server side, I am using the Nodejs Express framework with Socket.io. I have also installed client-side Socket.io for React. The basic implementation is all working fine. When there is a new user accessing the client-side app, Server-side socket.io listens to the connection. Any events triggered by the client also get reported on the server side. I am also able to broadcast the events back to all the connected clients using the socket.broadcast.emit() method.
I am trying to store the past events (basically, these are the invitations created by the currently connected players) in an array and then emit the stored array for the new connections so that the new users will see the past events(invitations). Below is my implementation on the server side:
//Array to store previously emitted events
const activeInvites = [];
//SocketIO connections
io.on("connection", (socket) => {
console.log(`⚡: ${socket.id} user just connected!`);
//Listen to the new invites
socket.on("newInvite", (invite) => {
activeInvites.push(invite);
socket.broadcast.emit("newPrivateInvites", invite);
});
//Publish all previously created invites to the new connections
io.emit("activeInvites", activeInvites); //new connections emit this event however the client won't listen to "activeInvites" event
socket.on("disconnect", () => {
console.log(`🔥: ${socket.id} user disconnected`);
destroy();
});
function destroy() {
try {
socket.disconnect();
socket.removeAllListeners();
socket = null; //this will kill all event listeners working with socket
//set some other stuffs to NULL
} catch (ex) {
console.error("Error destroying socket listeners:", ex.message);
}
}
});
And, below is my client-side implementation:
useEffect(() => {
socket.on("activeInvites", (invite) => {
console.log(invite);
}); //a new connection client skips listening to this event. Can't understand why.
socket.on("newPrivateInvites", (invite) => {
setPrivateInvites((existingInvites) => [...existingInvites, invite]);
});
//I have commented below code. Even if I uncomment it, no difference
// return () => {
// socket.off("newPrivateInvites");
// socket.off("activeInvites");
// socket.removeAllListeners();
// };
}, [socket, privateInvites]);
//Below is the handler function I use to open up a Sweetalert2 dialog to create an invite
const createMyGameInviteHandler = () => {
swalert
.fire({
title: "New Invite",
text: "This will create a new game invite and unique joining code that you can share with your friends",
iconHtml: '<img src="/images/invite.png" />',
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yeh! Let's Go!",
customClass: {
icon: "no-border",
},
})
.then((result) => {
if (result.isConfirmed) {
player.gameId = "1234";
setMyGameInvite(player);
socket.emit("newInvite", player); //This is where I create a new invitation event
}
});
};
In the above code, the "activeInvites" event is getting skipped by the new client even after socket.io on the server side triggers a new event after the new connection is created. Note that I am using io.emit() to emit the event to all the connected clients. So, even new clients should also listen. I am not able to see where the problem is. Could you please help me with this?
I tried to store the events generated by the client and consumed by the server in the past so that I could serve those events to the new clients when they establish the connection. I was expecting that io.emit() method would emit the event that will be consumed by all the clients including the new clients. However, new clients are skipping listening to this event. I am using useEffect hook in a react component.
I am generating data with a node.js simulator and passing this data to a http route /simulator/data
In the application I am listening broker with MQTT mqtthandler.js file which I share below.
//This is mqtthandler.js file
const mqtt = require("mqtt");
class MqttHandler {
constructor() {
this.mqttClient = null;
this.host = "mqtt://localhost:1883";
this.username = "YOUR_USER"; // mqtt credentials if these are needed to connect
this.password = "YOUR_PASSWORD";
}
connect() {
// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)
this.mqttClient = mqtt.connect(this.host, {
username: this.username,
password: this.password,
});
// Mqtt error calback
this.mqttClient.on("error", (err) => {
console.log(err);
this.mqttClient.end();
});
// Connection callback
this.mqttClient.on("connect", () => {
console.log(`mqtt client connected`);
});
// mqtt subscriptions
this.mqttClient.subscribe("value", { qos: 0 });
// When a message arrives, console.log it
this.mqttClient.on("message", function (topic, message) {
console.log(message.toString());
});
this.mqttClient.on("close", () => {
console.log(`mqtt client disconnected`);
});
}
// Sends a mqtt message to topic: mytopic
sendMessage(message) {
this.mqttClient.publish("value", message);
}
}
module.exports = MqttHandler;
When simulator sending the data to the /simulator/data route, I am getting the value and sending the broker with value topic. I share the post request code and output of simulator below.
var mqttHandler = require("../mqtthandler");
module.exports = function (app) {
app.get("/simulator", function (req, res) {
res.render("iot/simulator");
});
// route to display all the data that is generated
app.get("/simulator/data", require("./controllers/data").all);
var mqttClient = new mqttHandler();
mqttClient.connect();
// route to write data to the database
app.post(
"/simulator/data",
require("./controllers/data").write,
(req, res) => {
mqttClient.sendMessage(req.body.value);
res.status(200).send("Message sent to mqtt");
}
);
// delete the data when the stream is stopped or when the app is closed
app.get("/simulator/data/delete", require("./controllers/data").delete);
};
When I send get request to /simulator/data I am able to see generated data, however this data is not being sent to broker.
//This is output of simulator
[
{
"_id": "5ecfadc13cb66f10e4d9d39b",
"value": "1.886768240197795",
"__v": 0,
"categories": []
},
{
"_id": "5ecfadc23cb66f10e4d9d39c",
"value": "7.351404601932272",
"__v": 0,
"categories": []
}
]
PS: Broker is created via node-red
I would like to pass this data to broker and see the result with MQTT subscription. However I can not find where am I making mistake.
Your solution is to fix your development process. Rather than working from failure debugging 2 subsystems (your publisher / simulator and your subscriber), work from success:
1) use publishers that you KNOW work, eg. mosquitto_pub, any simulator that works, etc.
2) use subscribers that you KNOW work, eg. mosquitto_sub
This will solve your problem in minutes, rather than hours or days, and let you focus on the code that you REALLY want to develop.
So a couple of things to look at here.
Your this.mqttClient.subscribe() call is inline with your connect(), on.("error",...), on.("message",...) etc. So the subscribe() could fire before the connect() has finished...and thus you will never subscribe. Put the subscribe() inside the connect() block.
You are subscribing to "value", which is not a proper MQTT topic. If you must, use value/# for the subscribe(), and "value/.." for the publish(). Your class only allows for the single, hard-coded topic, so won't be very useful when you want to reuse that class for other projects. Take the time now to pass the topic string to the class as well.
It's been a while since I've worked with Node and Websockets. Basically how do I get socket.send() to work from another function is what I'm stuck on.
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', socket => {
socket.on('message', message => {
console.log(`received from a client: ${message}`);
});
socket.send('yo world!');
});
function onMessageHandler (target, context, msg, self) {
client.say(target, response);
server.socket.send(response);
console.log(response);
}
}
How do I get my onMessageHandler to trigger a socket send, this is fail... server.socket.send(response);
Seeing your question i think there is a lack of understanding on how Websockets work. I am assuming you're using https://github.com/websockets/ws
There are two things. First is the WebSocketerver which you've named as server and then an Individual Socket which you've named as socket
Now the thing to understand is socket is not accessible outside server.on() callback The reason for this is there could be 1000 of sockets connected at a given instance and there would be no way to uniquely identify a particular socket you want to send message to.
So ask yourself the question that your application wants to send message to an individual socket to send to everyone who is connected to your server (basically broadcast)
If you want to send to an individual, you will have to uniquely identify the user
this._wss = new WebSocket.Server({
port: ENV_APP_PORT_WS
});
this._wss.on("connection", async (ws: AppWebSocket, req: IncomingMessage) => {
// const ipAddress = req.connection.remoteAddress; // IP Address of User
logger.info(req);
const queryParams = url.parse(req.url, true).query;
let authUser: User;
try {
authUser = await this._authenticateWebSocket(queryParams);
} catch (e) {
// Terminate connection and return...
}
// WS User INIT
ws.isAlive = true;
ws.userId = authUser.id;
ws.uuid = Helpers.generateUUIDV4();
ws.send(JSON.stringify({
type: "connected",
env: ENV
}));
});
The above code will add a property to each socket object that will enable it to uniquely identify a particular user/socket.
While sending =>
onMessageHandler(targetUserId: number, message: string) {
const allSockets = <AppWebSocket[]>Array.from(this._wss.clients.values());
const targetSocket = allSockets.find(w => w.userId === targetUserId);
targetSocket.send(message);
}
If You want to send to all connect users, it's quite easy:
https://github.com/websockets/ws#server-broadcast
I'm trying to set up a realtime application using socket.io in Angular and node.js, which is not working as intended.
Whenever a client is making a new post, the other clients won't update until you interact with the client (e.g. clicking somewhere on the page, or clicking on the browsers tab).
However, having console open in the browser, I can see the new post in the console when I log the posts/objects - without the need to interact with the clients.
Angular:
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
posts: Post[] = [];
...
// Inside ngOnInit:
socket.on('data123', (res) => {
console.log('Updating list..', res);
this.postService.getPosts();
this.postsSub = this.postService.getPostUpdateListener()
.subscribe((posts: Post[]) => {
this.posts = posts;
});
});
Displaying in the template:
<... *ngFor="let item of posts">
Inside PostsService:
getPosts() {
this.http.get<{ message: string, posts: Post[] }>('http://localhost:3000/api/posts')
.subscribe((postData) => {
this.posts = postData.posts;
this.postsUpdate.next([...this.posts]);
});
}
Node.js - this socket.io solution is not yet sending the actual list:
const io = socket(server);
io.sockets.on('connection', (socket) => {
console.log(`new connection id: ${socket.id}`);
sendData(socket);
})
function sendData(socket){
socket.emit('data123', 'TODO: send the actual updated list');
setTimeout(() => {
console.log('sending to client');
sendData(socket);
}, 3000);
}
What worked as intended:
Using setInterval instead "socket.on(..)" on the front-end gave the intended result, meaning the clients will update automatically without the need of interacting. I'm fully aware this solution is horrible, but I assume this pinpointing that it's something wrong with socket solution above in Angular part.
wait, every time when socket.on('data123', (res) => {... you are creating new subscribe? it's wrong way...you must create subscribe in your socket connect feature
Currently having an issue with "Broadcast" not seeming to function properly with a super simple websocket setup I started in Node. This is my first time working with websockets so I may be missing something pretty apparent, but after looking online for a while I wasn't able to find a resolution.
Basically I am just trying to have the ability to push some json out to all currently connected Clients.
I can confirm that that socket is working because I am able to see the static connection string on 'ws.send' in the 'connection' block when I connect at ws://localhost:3000, as well as seeing mulitple clients logged out from the broadcast method if I connect with multiple clients.
Any help as to what I may be missing would be greatly appreciated,
var WebSocketServer = require('uws').Server;
var wss = new WebSocketServer({ port: 3000 }); // ws://localhost:3000
// Static test var
var test_message = {
'test': 'Response',
'test2': 'Response2'
};
// Broadcast to all.
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
console.log('IT IS GETTING INSIDE CLIENTS');
console.log(client);
// The data is coming in correctly
console.log(data);
client.send(data);
});
};
wss.on('connection', function(ws) {
ws.on('message', function(message) {
wss.broadcast(test_message);
console.log('Received: ' + message);
});
// TODO This is static just to check that the connection is properly working
ws.send('You successfully connected to the websocket.');
});
I tested your code with Smart Websocket Client. Your code is fine. If you broadcast data having string only, then you can see the reply in UI, but for javascript object, the client doesn't display although you can see response as Binary Frames (opcode = 2) in Chrome Developer Tools.
The reason behind this behavior is that the ws.send() method support normal strings, typed arrays or blobs, but sending typed arrays and blobs will result in the frame(s) received by the client as binary frames (opcode = 2).
You can try JSON.stringify the object,
wss.on('connection', function(ws) {
ws.on('message', function(message) {
wss.broadcast(JSON.stringify(test_message));
console.log('Received: ' + message);
});
ws.send('You successfully connected to the websocket.');
});