What is the use of dispatchEvent() in WebSocket? - javascript

I notice WebSocket extends EventTarget and EventTarget.dispatchEvent() "sends an Event to the object". But it is not clear from that MDN document who the object is.
I had thought maybe that object was the receiver on the WebSocket server side and dispatchEvent() is another way to send a message to the ws server like WebSocket.send()
So I did the following test in the browser console to see if that is the case(using https://github.com/websockets/ws for ws server).
const ws = new WebSocket('ws://localhost:3000');
//instead of using ws.send('hello world'); I use dispatchEvent here
//as I want to know what dispatchEvent does,
//e.g can it send my message to ws server like send() ?
ws.addEventListener('open', (evt) => {
let event = new MessageEvent('message', {
data: "hello world",
lastEventId: '101'
});
ws.dispatchEvent(event);
});
ws.addEventListener('message', (event) => {
console.log('Message from server ', event.data);
});
//Here is my server codes
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 3000 })
wss.on('connection', function connection(ws) {
console.log(`A ${ws.url}} connects`)
ws.on('message', (evt) => {
console.log(`on message:received: ${evt}`)
ws.send('test callback')
})
})
But as it turns out the console immediately prints out "Message from server hello world" while ws server didn't get that event.
So what is the use of dispatchEvent() in WebSocket ?

The EventTarget interface is implemented by objects that can receive events, and is an interface that your ws (WebSocket) variable will implement to receive communication events from the low-level browser implementation of that network socket (such as when it successfully opens, and when a message is received).
However, you're not supposed to call it yourself. What you're doing is manually feeding it an event, and causing the WebSocket to behave as if it was an real event that has arrived from the remote server.
Therefore, your code is only useful in testing scenarios where you are emulating a server response.

Related

Why is a connection not created for this simple websocket snippet?

I have a simple snippet on the front end as follows which I can verify is working. I can do this by changing the port to something other than 3000 and it will error.
It is definitely finding the server at that port:
// Create WebSocket connection .. will error if I change the port
const socket = new WebSocket('ws://localhost:3000');
console.log('DEBUG: Web socket is up: ');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
I am using ws-express on the server side as follows. This was the minimal example given in the NPM docs:
const expressWs = require('express-ws')(app);
app.ws('/echo', (ws, req) => {
ws.on('message', (msg) => {
ws.send(msg);
});
});
However, the open event on the client never fires. I would like to send messages from the client to the server, but I assume, that I need an open event to fire first.

Client receives only some server sent events

I am working with server sent events with spring boot in the backend and react-native/expo as client.
To handle server send event on the client I use react-native-event-source. But the problem is only some events are received by the client. Here is the scenario:
event id 0 -> not received
event id 1 -> received
event id 2 -> not received
event id 3 -> received
event id 4 -> not received
event id 5 -> received
...
on the server side we tried deferent hacks and logs show that the server is actually sending each event but the expo client seems to ignore some events (1 event in two).
Here is how we initialize connection to get server sent events:
listen() {
const options = {
headers: {
Authorization: `Bearer ${this.userToken}`,
},
};
let url = `${TEST_URL}${this.user.id}`;
try {
console.log('starting stream ...');
const eventName = `dataSet-created${this.user.id}`;
this.eventSource = new RNEventSource(url, options);
this.eventSource.addEventListener(eventName, event => {
console.log('stream event received', event);
});
this.eventSource.addEventListener('error', e => {
console.log('stream listener error', e);
});
console.log('eventSource stream ...', this.eventSource);
} catch (e) {
console.log('start stream error', e);
}
}
react-native-event-source does not use true EventSource but rather works around by polling. So my guess is your client miss messages because there are multiple in single polling interval (that is 500ms).
Reason for this is react native currently does not support HTTP streams.

MQTT ACK implementation - event listening break

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.

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.

How to call a function from outside the object

I am trying to make a proxy between sockets and websockets. I receive requests on socket server and I want to proxy them to websocket clients, however websocket.ws.send appears to be undefined. What would be the correct way of doing this? How to call ws.send() from outside of the object?
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({port: 8001}),
var net = require('net')
websocket = wss.on('connection', function(ws) {
ws.on('message', function(message) {
console.log('received: %s', message);
});
ws.send("NEW USER JOINED");
});
var socketServer = net.createServer(function(socket) {
socket.on("data", function(data){
console.log("Received: "+data)
websocket.ws.send("Message")
});
});
socketServer.listen(1337, '127.0.0.1');
The problem you have is that ws is a single web socket for one connection. It is not a property on the websocket object, but an argument to a callback you define. Each time someone connects you will get a connection event which will execute your callback, allowing you to bind a function to that specific web socket for the message event.
You either need to store the ws object in a variable that the socket server can access or you could broadcast to all clients.
wss.clients.forEach(function each(client) {
client.send(data);
});
This will work fine if you only have one client, however if you have multiple connections to both the websocket and socket server then you need to use a method of identifying connections on both so that you can locate the websocket connection you need to send data to.

Categories